mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 01:31:10 +00:00
Merge 8966a29283 into ad30010f80
This commit is contained in:
@@ -233,12 +233,17 @@ QString lidlMakeSource(const ModuleDecl& module, BindMode bindMode)
|
||||
if (ret != "void") s << " QVariant _result = ";
|
||||
else s << " ";
|
||||
|
||||
s << "m_client->invokeRemoteMethod(" << targetExpr << ", \"" << md.name << "\", QVariantList{";
|
||||
// Pack each argument as ONE element via packVariantList (which wraps
|
||||
// with QVariant::fromValue). A braced `QVariantList{v}` or `<< v` would
|
||||
// CONCATENATE a QVariantList-typed arg (any `[T]` list) into the args
|
||||
// list, sending a 3-element [1,2,3] as three positional args instead of
|
||||
// one — the historical "typed arrays empty over the Qt path" bug.
|
||||
s << "m_client->invokeRemoteMethod(" << targetExpr << ", \"" << md.name << "\", packVariantList(";
|
||||
for (int i = 0; i < nParams; ++i) {
|
||||
s << md.params[i].name;
|
||||
if (i + 1 < nParams) s << ", ";
|
||||
}
|
||||
s << "}, Timeout(), &_err);\n";
|
||||
s << "), Timeout(), &_err);\n";
|
||||
s << " if (err) *err = _err;\n";
|
||||
s << " else if (!_err.ok()) qWarning() << \"" << className << "::" << md.name
|
||||
<< ": remote call failed:\" << QString::fromStdString(_err.message);\n";
|
||||
@@ -255,19 +260,14 @@ QString lidlMakeSource(const ModuleDecl& module, BindMode bindMode)
|
||||
if (nParams > 0) s << ", ";
|
||||
s << "std::function<void(" << (ret == "void" ? "void" : ret) << ")> callback, Timeout timeout) {\n";
|
||||
s << " if (!callback) return;\n";
|
||||
s << " m_client->invokeRemoteMethodAsync(" << targetExpr << ", \"" << md.name << "\", ";
|
||||
if (nParams == 0) {
|
||||
s << "QVariantList()";
|
||||
} else if (nParams == 1) {
|
||||
s << "QVariantList() << " << md.params[0].name;
|
||||
} else {
|
||||
s << "QVariantList{";
|
||||
for (int i = 0; i < nParams; ++i) {
|
||||
s << md.params[i].name;
|
||||
if (i + 1 < nParams) s << ", ";
|
||||
}
|
||||
s << "}";
|
||||
// Same one-element-per-arg packing as the sync path (see above): a
|
||||
// QVariantList-typed arg must not be spread across the args list.
|
||||
s << " m_client->invokeRemoteMethodAsync(" << targetExpr << ", \"" << md.name << "\", packVariantList(";
|
||||
for (int i = 0; i < nParams; ++i) {
|
||||
s << md.params[i].name;
|
||||
if (i + 1 < nParams) s << ", ";
|
||||
}
|
||||
s << ")";
|
||||
s << ", [callback](QVariant v) {\n";
|
||||
if (ret == "void") {
|
||||
s << " callback();\n";
|
||||
|
||||
@@ -613,9 +613,15 @@ QString makeSource(const QString& moduleName, const QString& className, const QS
|
||||
if (ret != "void") s << " QVariant _result = ";
|
||||
else s << " ";
|
||||
|
||||
// Wrap each argument in QVariant::fromValue so it becomes exactly ONE
|
||||
// element of the args list. A bare `QVariantList{v}` CONCATENATES a
|
||||
// QVariantList-typed arg (every `[T]` list) into the args list — sending
|
||||
// a 3-element [1,2,3] as three positional args — the historical "typed
|
||||
// arrays empty over the Qt path" bug. fromValue does not double-wrap an
|
||||
// already-QVariant (`any`) arg.
|
||||
s << "m_client->invokeRemoteMethod(" << targetExpr << ", \"" << name << "\", QVariantList{";
|
||||
for (int i = 0; i < params.size(); ++i) {
|
||||
s << wireArg(params.at(i).toObject());
|
||||
s << "QVariant::fromValue(" << wireArg(params.at(i).toObject()) << ")";
|
||||
if (i + 1 < params.size()) s << ", ";
|
||||
}
|
||||
s << "}, Timeout(), &_err);\n";
|
||||
@@ -672,9 +678,11 @@ QString makeSource(const QString& moduleName, const QString& className, const QS
|
||||
if (params.size() == 0) {
|
||||
s << "QVariantList()";
|
||||
} else {
|
||||
// Same one-element-per-arg wrapping as the sync path (see above): a
|
||||
// QVariantList-typed arg must not be spread across the args list.
|
||||
s << "QVariantList{";
|
||||
for (int i = 0; i < params.size(); ++i) {
|
||||
s << wireArg(params.at(i).toObject());
|
||||
s << "QVariant::fromValue(" << wireArg(params.at(i).toObject()) << ")";
|
||||
if (i + 1 < params.size()) s << ", ";
|
||||
}
|
||||
s << "}";
|
||||
@@ -867,6 +875,12 @@ static QString lpFromJsonExpr(const QString& qtType, const QString& jv)
|
||||
if (t == "bool") return "(" + jv + ".is_boolean() ? " + jv + ".get<bool>() : false)";
|
||||
if (t == "std::vector<std::string>") return "logos::jsonToStringVec(" + jv + ")";
|
||||
if (t == "std::vector<uint8_t>") return "logos::jsonToBytes(" + jv + ")";
|
||||
// `any` (QVariant) is a raw json value of ANY shape — pass it through
|
||||
// unchanged. It shares the LogosMap std type with the `{tstr:any}` map
|
||||
// (QVariantMap), but only the map is forced to an object below; forcing
|
||||
// `any` to an object collapsed every non-object value (a string, a number,
|
||||
// an array) to `{}` (e.g. a proxy forwarding echoAny returned {} for "x").
|
||||
if (mapReturnType(qtType) == "QVariant") return jv;
|
||||
if (t == "LogosMap") return "(" + jv + ".is_object() ? " + jv + " : LogosMap::object())";
|
||||
if (t == "LogosList") return "(" + jv + ".is_array() ? " + jv + " : LogosList::array())";
|
||||
if (t == "StdLogosResult") return "logos::jsonToStdResult(" + jv + ")";
|
||||
|
||||
@@ -198,8 +198,37 @@ TEST(LidlGenClient, MethodWithManyParams)
|
||||
m.methods.push_back(md);
|
||||
|
||||
QString s = lidlMakeSource(m);
|
||||
// >5 params should use QVariantList{} syntax
|
||||
EXPECT_TRUE(s.contains("QVariantList{"));
|
||||
// Args are packed via packVariantList (one QVariant element per arg),
|
||||
// regardless of arity — never a braced/`<<` list that would spread a
|
||||
// QVariantList-typed arg.
|
||||
EXPECT_TRUE(s.contains("packVariantList(p0, p1, p2, p3, p4, p5, p6)"));
|
||||
}
|
||||
|
||||
// Regression: a QVariantList-typed ([any]/[int]/...) argument must be packed as
|
||||
// ONE element, not concatenated into the args list. `QVariantList{v}` and
|
||||
// `QVariantList() << v` both spread a QVariantList; packVariantList wraps each
|
||||
// arg with QVariant::fromValue, so a single list arg stays a single arg.
|
||||
TEST(LidlGenClient, ListArgIsPackedAsOneElement)
|
||||
{
|
||||
ModuleDecl m;
|
||||
m.name = "arrs";
|
||||
MethodDecl md;
|
||||
md.name = "echoList";
|
||||
TypeExpr elem = { TypeExpr::Primitive, "any", {} };
|
||||
md.returnType = { TypeExpr::Array, "", { elem } };
|
||||
ParamDecl p;
|
||||
p.name = "v";
|
||||
p.type = { TypeExpr::Array, "", { elem } };
|
||||
md.params.push_back(p);
|
||||
m.methods.push_back(md);
|
||||
|
||||
QString s = lidlMakeSource(m);
|
||||
// Both sync and async pack the single list arg via packVariantList(v).
|
||||
EXPECT_TRUE(s.contains("invokeRemoteMethod(\"arrs\", \"echoList\", packVariantList(v)"));
|
||||
EXPECT_TRUE(s.contains("invokeRemoteMethodAsync(\"arrs\", \"echoList\", packVariantList(v)"));
|
||||
// Guard against the spreading forms regressing back in.
|
||||
EXPECT_FALSE(s.contains("QVariantList{v}"));
|
||||
EXPECT_FALSE(s.contains("QVariantList() << v"));
|
||||
}
|
||||
|
||||
TEST(LidlGenClient, VoidReturnMethod)
|
||||
|
||||
@@ -53,7 +53,7 @@ TEST(MakeSourceTest, OneParam)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("fn", "bool", 1));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"fn\", QVariantList{p0}, Timeout(), &_err)"));
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"fn\", QVariantList{QVariant::fromValue(p0)}, Timeout(), &_err)"));
|
||||
EXPECT_TRUE(src.contains("return _result.toBool()"));
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ TEST(MakeSourceTest, TwoParams)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("fn", "void", 2));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"fn\", QVariantList{p0, p1}, Timeout(), &_err)"));
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"fn\", QVariantList{QVariant::fromValue(p0), QVariant::fromValue(p1)}, Timeout(), &_err)"));
|
||||
}
|
||||
|
||||
TEST(MakeSourceTest, ThreeParams)
|
||||
@@ -70,7 +70,7 @@ TEST(MakeSourceTest, ThreeParams)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("fn", "QString", 3));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("p0, p1, p2"));
|
||||
EXPECT_TRUE(src.contains("QVariant::fromValue(p0), QVariant::fromValue(p1), QVariant::fromValue(p2)"));
|
||||
EXPECT_TRUE(src.contains("return _result.toString()"));
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ TEST(MakeSourceTest, FourParams)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("fn", "double", 4));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("p0, p1, p2, p3"));
|
||||
EXPECT_TRUE(src.contains("QVariant::fromValue(p0), QVariant::fromValue(p1), QVariant::fromValue(p2), QVariant::fromValue(p3)"));
|
||||
EXPECT_TRUE(src.contains("return _result.toDouble()"));
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ TEST(MakeSourceTest, FiveParams)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("fn", "float", 5));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("p0, p1, p2, p3, p4"));
|
||||
EXPECT_TRUE(src.contains("QVariant::fromValue(p0), QVariant::fromValue(p1), QVariant::fromValue(p2), QVariant::fromValue(p3), QVariant::fromValue(p4)"));
|
||||
EXPECT_TRUE(src.contains("return _result.toFloat()"));
|
||||
}
|
||||
|
||||
@@ -97,10 +97,70 @@ TEST(MakeSourceTest, MoreThanFiveParamsUsesVariantList)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("fn", "QVariant", 6));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("QVariantList{p0, p1, p2, p3, p4, p5}"));
|
||||
EXPECT_TRUE(src.contains("QVariantList{QVariant::fromValue(p0), QVariant::fromValue(p1), QVariant::fromValue(p2), QVariant::fromValue(p3), QVariant::fromValue(p4), QVariant::fromValue(p5)}"));
|
||||
EXPECT_TRUE(src.contains("return _result"));
|
||||
}
|
||||
|
||||
// Regression: a QVariantList-typed ([any]/[int]/...) argument must be wrapped as
|
||||
// ONE element via QVariant::fromValue. A bare `QVariantList{v}` concatenates the
|
||||
// list's elements into the args list, sending [1,2,3] as three positional args
|
||||
// — the "typed arrays empty over the Qt path" bug.
|
||||
TEST(MakeSourceTest, ListArgWrappedAsOneElement)
|
||||
{
|
||||
QJsonObject m;
|
||||
m["name"] = "echoList";
|
||||
m["returnType"] = "QVariantList";
|
||||
m["isInvokable"] = true;
|
||||
QJsonObject p;
|
||||
p["type"] = "QVariantList";
|
||||
p["name"] = "v";
|
||||
QJsonArray params;
|
||||
params.append(p);
|
||||
m["parameters"] = params;
|
||||
QJsonArray methods;
|
||||
methods.append(m);
|
||||
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("invokeRemoteMethod(\"mod\", \"echoList\", QVariantList{QVariant::fromValue(v)}, Timeout(), &_err)"));
|
||||
EXPECT_TRUE(src.contains("invokeRemoteMethodAsync(\"mod\", \"echoList\", QVariantList{QVariant::fromValue(v)}"));
|
||||
// The bare (spreading) form must not appear.
|
||||
EXPECT_FALSE(src.contains("QVariantList{v}"));
|
||||
}
|
||||
|
||||
// Regression: in the Qt-free (lp) wrapper, an `any` (QVariant) return must pass
|
||||
// the raw json value through, NOT force it to an object. `any` shares the
|
||||
// LogosMap std type with the `{tstr:any}` map, and forcing `any` to an object
|
||||
// collapsed every non-object value to `{}` (e.g. a proxy forwarding echoAny
|
||||
// returned {} for the string "x"). The map keeps its object coercion.
|
||||
TEST(MakeSourceTest, LpAnyReturnPassesThroughButMapForcesObject)
|
||||
{
|
||||
QJsonObject any;
|
||||
any["name"] = "echoAny";
|
||||
any["returnType"] = "QVariant";
|
||||
any["isInvokable"] = true;
|
||||
{
|
||||
QJsonObject p; p["type"] = "QVariant"; p["name"] = "v";
|
||||
QJsonArray ps; ps.append(p); any["parameters"] = ps;
|
||||
}
|
||||
QJsonObject mp;
|
||||
mp["name"] = "echoMap";
|
||||
mp["returnType"] = "QVariantMap";
|
||||
mp["isInvokable"] = true;
|
||||
{
|
||||
QJsonObject p; p["type"] = "QVariantMap"; p["name"] = "v";
|
||||
QJsonArray ps; ps.append(p); mp["parameters"] = ps;
|
||||
}
|
||||
QJsonArray methods;
|
||||
methods.append(any);
|
||||
methods.append(mp);
|
||||
|
||||
QString src = makeSourceLp("mod", "Mod", "mod.h", methods);
|
||||
// `any` return: raw passthrough (return _r;), no is_object coercion.
|
||||
EXPECT_TRUE(src.contains("return _r;"));
|
||||
// `{tstr:any}` map return: still forced to an object.
|
||||
EXPECT_TRUE(src.contains("_r.is_object() ? _r : LogosMap::object()"));
|
||||
}
|
||||
|
||||
TEST(MakeSourceTest, VoidReturnNoConversion)
|
||||
{
|
||||
QJsonArray methods;
|
||||
|
||||
Reference in New Issue
Block a user