From 03d1654e0002c6abfe80da8217c25296169a5c3d Mon Sep 17 00:00:00 2001 From: Canrad <1517807724@qq.com> Date: Fri, 30 Oct 2026 09:09:19 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(renamer):=20shadow=20token=20=E4=B8=AD?= =?UTF-8?q?=E6=AE=B5=E8=A3=81=E5=AE=9A=E2=80=94=E2=80=94=E6=AE=B5=E5=A4=96?= =?UTF-8?q?=E8=8A=B1=E6=8B=AC=E5=8F=B7=20fail-loud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §4 落地:${ 出现在类型名/操作数中段(非段首)不展开、不静默——二进制 名文法不含花括号,未标记的引用形状混入裸名属调用方笔误。Descriptor 槽 类型段与 JasmBody 裸引用部(含 owner/name 分段后逐段裁定,段首 ${ 句柄路径不受影响)→ malformed-handle 带 $$ 转义指引;$${ 显式转义 两槽均保留字面。name 槽维持整串二选一不引入中段扫描:Raw 恒等回显 (verifyRaw=false 直通位)或核表 raw-not-found 响亮,零漂移不变。 --- mjbase/src/mjbase/renamer/shadow_token.cpp | 41 ++++++++++++++ mjbase/src/mjbase/renamer/shadow_token.hpp | 6 +++ .../unittest/renamer/shadow_token_test.cpp | 53 +++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/mjbase/src/mjbase/renamer/shadow_token.cpp b/mjbase/src/mjbase/renamer/shadow_token.cpp index e92d6aa..07e6881 100644 --- a/mjbase/src/mjbase/renamer/shadow_token.cpp +++ b/mjbase/src/mjbase/renamer/shadow_token.cpp @@ -389,6 +389,19 @@ ShadowSlotResult resolveShadowDescriptor(const ShadowAliasTable& table, const auto parts = parseShadowRef(content); switch (parts.form) { case ShadowRefForm::Raw: + // 类型段中段的 `${`(非段首):二进制名文法不含花括号, + // 未标记的引用形状混入类型名 = 调用方笔误 → fail-loud, + // 不静默替换(EscapedLiteral 显式转义另案,保留字面)。 + if (content.find('{') != std::string::npos || + content.find('}') != std::string::npos) { + result = failure(ShadowSlotError::MalformedHandle, + "internal name segment " + quoted(content) + + " in descriptor " + quoted(input) + + " contains brace characters (unmarked " + "reference mid-segment); place the handle " + "at the segment start or escape with $$"); + return false; + } out += 'L'; out += content; out += ';'; @@ -626,13 +639,41 @@ ShadowSlotResult resolveShadowJasmBody(const ShadowAliasTable& table, } else { // Raw:复合 owner.name(name 可嵌 MemberRef handle)或纯裸名。 + // 花括号裁定按段进行:段首 ${ 走句柄路径,其余位置的花括号 = + // 未标记引用中段,fail-loud(同 Descriptor 槽类型段;显示文本的 + // shadow 复合名不含 ${ 形状,零漂移不受损)。 + const auto bracesIn = [](const std::string& segment) { + return segment.find('{') != std::string::npos || + segment.find('}') != std::string::npos; + }; const auto dot = refPart.find('.'); if (dot == std::string::npos) { + if (bracesIn(refPart)) { + return failure(ShadowSlotError::MalformedHandle, + "raw reference " + quoted(refPart) + + " in jasm operand contains brace characters " + "(unmarked reference mid-segment); place the " + "handle at the segment start or escape with $$"); + } resolvedRef = refPart; // 类操作数裸名:零漂移直通 } else { const auto ownerPart = refPart.substr(0, dot); const auto namePart = refPart.substr(dot + 1); + if (ownerPart.rfind("${", 0) != 0 && bracesIn(ownerPart)) { + return failure(ShadowSlotError::MalformedHandle, + "raw owner " + quoted(ownerPart) + + " in jasm operand contains brace characters " + "(unmarked reference mid-segment); place the " + "handle at the segment start or escape with $$"); + } + if (bracesIn(namePart) && namePart.rfind("${", 0) != 0) { + return failure(ShadowSlotError::MalformedHandle, + "raw member name " + quoted(namePart) + + " in jasm operand contains brace characters " + "(unmarked reference mid-segment); place the " + "handle at the segment start or escape with $$"); + } std::string resolvedName; if (!resolveNamePart(namePart, ownerPart, resolvedName)) return result; resolvedRef = ownerPart + "." + resolvedName; diff --git a/mjbase/src/mjbase/renamer/shadow_token.hpp b/mjbase/src/mjbase/renamer/shadow_token.hpp index 3772a91..90c0af8 100644 --- a/mjbase/src/mjbase/renamer/shadow_token.hpp +++ b/mjbase/src/mjbase/renamer/shadow_token.hpp @@ -8,6 +8,12 @@ /// §3 位置三分 解析是文法驱动的:展开只发生在文法标识槽;字符串常量/注释 /// 恒等、永不解析(文法上不是标识槽,本收口根本不会被调用—— /// 该性质由测试钉住,而非运行时检查)。 +/// §4 中段裁定 `${` 出现在类型名/操作数中段(非段首)不展开、不静默: +/// 二进制名文法不含花括号,未标记的引用形状混入裸名 = +/// 调用方笔误 → Descriptor 类型段与 JasmBody 裸引用部 +/// fail-loud(malformed-handle);name 槽保持整串二选一 +/// (raw 零漂移,verifyRaw 提供核表响亮路径);`$${` 显式 +/// 转义两槽均保留字面(调用方自证字面意图)。 /// §6 错误四分类 unknown-handle / malformed-handle / raw-not-found / /// ambiguous-member,各自带固定修复动作;缺 sidecar 时任何引用 /// 形状 → 硬报错(missing-sidecar),不静默回落裸名。 diff --git a/mjbase/test/unittest/renamer/shadow_token_test.cpp b/mjbase/test/unittest/renamer/shadow_token_test.cpp index 59a5847..f933ee6 100644 --- a/mjbase/test/unittest/renamer/shadow_token_test.cpp +++ b/mjbase/test/unittest/renamer/shadow_token_test.cpp @@ -662,4 +662,57 @@ TEST(MjShadowTokenTest, JasmBodyGrammarGarbageFailsLoud) EXPECT_EQ(r.raw, ""); } +// ── §4 中段裁定(${ 不在段首:不展开、不静默)──────────────────── + +TEST(MjShadowTokenTest, MidSegmentBracesInDescriptorFailLoud) +{ + const auto table = makeTable(); + // 类型段中段的 ${(非段首):二进制名不含花括号 → malformed-handle, + // 不静默替换成含 ${ 字面的「合法描述符」。 + const auto r = resolveDesc(table, "(Lfoo${Class_1.11111111};)V"); + EXPECT_EQ(r.error, ShadowSlotError::MalformedHandle); + EXPECT_NE(std::string::npos, text(r).find("brace characters")); + + // 孤立花括号同判(同一文法违约,不只盯 ${ 二字面)。 + EXPECT_EQ(resolveDesc(table, "(Lfo{o};)V").error, ShadowSlotError::MalformedHandle); + + // $${ 显式转义保留字面(调用方自证意图,不属中段违约)。 + const auto escaped = resolveDesc(table, "(L$${a/b/Weird};)V"); + ASSERT_EQ(escaped.error, ShadowSlotError::None) << text(escaped); + EXPECT_EQ(escaped.raw, "(L${a/b/Weird};)V"); +} + +TEST(MjShadowTokenTest, NameSlotMidSegmentStaysRawZeroDrift) +{ + const auto table = makeTable(); + // name 槽整串二选一(§3):${ 不在串首 → 整串 Raw(无中段扫描), + // 解析层不展开、不判 malformed;响亮路径只来自核表位(verifyRaw): + // 默认查询核表 → raw-not-found;直通位(verifyRaw=false)恒等回显。 + const auto checked = resolve(table, "foo${Class_1.11111111}"); + EXPECT_EQ(checked.error, ShadowSlotError::RawNotFound); + + ShadowSlotQuery passthrough; + passthrough.verifyRaw = false; + const auto echoed = resolveShadowSlot(table, "foo${Class_1.11111111}", passthrough); + ASSERT_EQ(echoed.error, ShadowSlotError::None) << text(echoed); + EXPECT_EQ(echoed.raw, "foo${Class_1.11111111}"); + EXPECT_FALSE(echoed.fromHandle); +} + +TEST(MjShadowTokenTest, JasmBodyMidSegmentBracesFailLoud) +{ + const auto table = makeTable(); + // 裸引用部中段的 ${ → malformed-handle(同 Descriptor 槽类型段)。 + EXPECT_EQ(resolveJasm(table, "foo${Class_1.11111111}.fly ()V").error, + ShadowSlotError::MalformedHandle); + EXPECT_EQ(resolveJasm(table, "foo${Class_1.11111111}").error, + ShadowSlotError::MalformedHandle); + + // $${ 显式转义 → 字面直通(剥 $ 后原样回显,零漂移)。 + const auto escaped = resolveJasm(table, "$${a/b/Weird}.fly ()V"); + ASSERT_EQ(escaped.error, ShadowSlotError::None) << text(escaped); + EXPECT_EQ(escaped.raw, "${a/b/Weird}.fly ()V"); + EXPECT_FALSE(escaped.fromHandle); +} + } // namespace mjbase::renamer From 21cdc22a2d4da9af9e91a6d3801e0a975b2019da Mon Sep 17 00:00:00 2001 From: Canrad <1517807724@qq.com> Date: Fri, 30 Oct 2026 10:51:59 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix(build):=20libjhasm=200.3.3=20=E6=8E=A5?= =?UTF-8?q?=E7=BA=BF=E2=80=94=E2=80=94=E8=A1=A5=E4=B8=8A=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E9=92=89=EF=BC=880.3.2=E2=86=920.3.3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4d7dcc6 提交信息声称 0.3.3 接线,但根 xmake.lua 的 add_requires 版本钉 未随之升级,仍解析 0.3.2:孤立代理反汇编回归 3 例在 main CI 与本地全量 里失败。补钉后 delete-then-rebuild 重建门全量 1903 例 0 失败。 --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index 08eab28..be73cd6 100644 --- a/xmake.lua +++ b/xmake.lua @@ -105,7 +105,7 @@ add_requires("libgarena 0.0.3") add_requires("libjclassfile 0.0.2") add_requires("libjstd 0.0.1") add_requires("libjdk_finder 0.1.0") -add_requires("libjhasm 0.3.2") +add_requires("libjhasm 0.3.3") -- 嵌套 libca 全路径钉定,缺一条就多一个实例。 add_requireconfs("libjbc.libca", {version = LUIOX_LIBCA_VERSION, configs = {modules = LUIOX_LIBCA_MODULES}}) add_requireconfs("libjstr.libca", {version = LUIOX_LIBCA_VERSION, configs = {modules = LUIOX_LIBCA_MODULES}}) From d0083dfacb353a1836957eaa22292dcfa29b59ba Mon Sep 17 00:00:00 2001 From: Canrad <1517807724@qq.com> Date: Fri, 30 Oct 2026 10:52:12 +0800 Subject: [PATCH 3/4] =?UTF-8?q?test(renamer):=20JasmBody=20=E5=A4=8D?= =?UTF-8?q?=E5=90=88=E6=93=8D=E4=BD=9C=E6=95=B0=E7=9F=A9=E9=98=B5=20+=20ow?= =?UTF-8?q?ner=20=E5=86=85=E7=82=B9=E5=BD=A2=E7=8A=B6=20fail-loud=20?= =?UTF-8?q?=E9=92=89=E6=89=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 矩阵钉住中段 guard 与合法嵌套形态的边界:raw owner + 嵌套成员句柄 (方法/字段 × 带/不带描述符)、句柄 owner 复合、描述符多类型段/数组段 内嵌句柄、shadow 显示复合名零漂移直通——全部正常展开或直通。 owner 段内含 '.' 的复合名(显示形 owner + 嵌套句柄):JasmBody 槽契约 OWNER = 裸内部名('/' 分包),按首个 '.' 分段后嵌套句柄落于 name 段 中段,中段 guard fail-loud;即使改在 .${ 边界分段,dotted owner 与 表内句柄 owner 也不自洽,同为响亮报错。钉住现行语义(消息带 $$ 转义 指引),不改 guard 顺序。 --- .../unittest/renamer/shadow_token_test.cpp | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/mjbase/test/unittest/renamer/shadow_token_test.cpp b/mjbase/test/unittest/renamer/shadow_token_test.cpp index f933ee6..e2b032f 100644 --- a/mjbase/test/unittest/renamer/shadow_token_test.cpp +++ b/mjbase/test/unittest/renamer/shadow_token_test.cpp @@ -715,4 +715,71 @@ TEST(MjShadowTokenTest, JasmBodyMidSegmentBracesFailLoud) EXPECT_FALSE(escaped.fromHandle); } +// ── §4 补钉:中段 guard × 合法嵌套形态矩阵 + owner 内点形状钉扎 ────── + +TEST(MjShadowTokenTest, JasmBodyCompositeOperandMatrix) +{ + const auto table = makeTable(); + const std::string cr = kClassRef; // ${Class_1.11111111} + const std::string mr = kMemberRef; // ${Class_1.11111111.m_1.22222222} + const std::string fmr = "${Class_1.11111111.f_1.33333333}"; + struct Case + { + std::string in; + bool legal; + std::string raw; + }; + const Case cases[] = { + // 合法:raw owner + 嵌套成员句柄(方法/字段 × 带/不带描述符)。 + {"a/b/Alpha." + mr + " ()V", true, "a/b/Alpha.fly ()V"}, + {"a/b/Alpha." + mr, true, "a/b/Alpha.fly"}, + {"a/b/Alpha." + fmr + " I", true, "a/b/Alpha.weight I"}, + {"a/b/Alpha." + fmr, true, "a/b/Alpha.weight"}, + // 合法:句柄 owner 复合与描述符内嵌句柄(多类型段/数组段)。 + {cr + "." + mr + " ()V", true, "a/b/Alpha.fly ()V"}, + {"a/b/Alpha.fly (L" + cr + ";L${Class_2.44444444};)V", true, + "a/b/Alpha.fly (La/b/Alpha;La/b/Beta;)V"}, + {"a/b/Alpha.weight [L" + cr + ";", true, "a/b/Alpha.weight [La/b/Alpha;"}, + // 合法:显示文本 shadow 复合名(无 ${ 形状)零漂移直通。 + {"Class_1.m_1 ()V", true, "Class_1.m_1 ()V"}, + {"Class_1.m_1", true, "Class_1.m_1"}, + // 非法:owner 中段混入花括号(调用方笔误,fail-loud)。 + {"a/b/Al${pha.fly ()V", false, ""}, + }; + for (const auto& c : cases) { + const auto r = resolveJasm(table, c.in); + if (c.legal) { + EXPECT_EQ(r.error, ShadowSlotError::None) << c.in << " -> " << text(r); + if (r.error == ShadowSlotError::None) EXPECT_EQ(r.raw, c.raw) << c.in; + } + else { + EXPECT_EQ(r.error, ShadowSlotError::MalformedHandle) << c.in << " -> " << text(r); + } + } +} + +TEST(MjShadowTokenTest, JasmBodyDottedOwnerWithNestedHandleFailsLoud) +{ + const auto table = makeTable(); + // owner 段内含 '.' 的复合名(显示形 owner + 嵌套成员句柄):JasmBody + // 槽契约的 OWNER 是裸内部名('/' 分包,不含 '.')——按首个 '.' 分段后 + // 嵌套句柄落于 name 段中段,中段 guard fail-loud;即使改在 ".${" 边界 + // 分段,dotted owner 与句柄 owner(表内内部名形)也不自洽,仍 fail-loud。 + // 两条路同为响亮报错,本测试钉住现行 guard 语义(消息带 $$ 转义指引)。 + const std::string mr = kMemberRef; + const std::string fmr = "${Class_1.11111111.f_1.33333333}"; + const std::string inputs[] = { + "a.b.Alpha." + mr + " ()V", + "a.b.Alpha." + mr, + "a.b.Alpha." + fmr + " I", + }; + for (const auto& in : inputs) { + const auto r = resolveJasm(table, in); + EXPECT_EQ(r.error, ShadowSlotError::MalformedHandle) << in << " -> " << text(r); + if (r.error == ShadowSlotError::MalformedHandle) { + EXPECT_NE(std::string::npos, text(r).find("brace characters")) << in; + } + } +} + } // namespace mjbase::renamer From e8cfead19bcd9dd8de0e9a0ee174453f900fb0b2 Mon Sep 17 00:00:00 2001 From: Canrad <1517807724@qq.com> Date: Fri, 30 Oct 2026 11:48:07 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix(build):=20libjbc=20=E5=9B=9E=E8=90=BD?= =?UTF-8?q?=200.4.22=20=E5=AF=B9=E9=BD=90=20libjhasm=200.3.3=20=E6=B2=99?= =?UTF-8?q?=E7=AE=B1=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libjhasm 0.3.3(e4fb91f)自带内联 libjbc 定义最高到 0.4.22(其自身 钉定版),包沙箱按内联定义解析后端依赖;顶层 0.4.23 被合并进沙箱需求 后在内联定义中查无版本映射,xmake 以版本号作 git ref 直取 libjbc 仓 ——远端无该 ref,Configure 期确定性失败(双平台)。回落 0.4.22 并对 libjhasm.libjbc 嵌套 dep 加显式 requireconf 钉定,保持全消费图单实例。 0.4.23 为上游仓库清理/测试收编批,消费面无功能差异。 清空本地全局包库 libjbc/libjhasm 强制从源解析(与 CI 干净机同路径) 两包 install ok;delete-then-rebuild 重建门全量 1903 例 0 失败。 --- xmake.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index be73cd6..45cc77e 100644 --- a/xmake.lua +++ b/xmake.lua @@ -98,7 +98,12 @@ LUIOX_LIBCA_MODULES = "core,crypto,str,fs,env,io,json,net,http,process,opt,threa -- 其嵌套 libca 由定义内的 add_deps 钉到上面的单源版本,故两处必须同一字面量。 includes("xmake/packages/libjbc.lua") add_requires("libca " .. LUIOX_LIBCA_VERSION, {configs = {modules = LUIOX_LIBCA_MODULES}}) -add_requires("libjbc 0.4.23") +-- libjbc 上界对齐 libjhasm 沙箱:libjhasm 0.3.3 自带内联 libjbc 定义只到 +-- 0.4.22(其自身钉定版),包沙箱按内联定义解析,顶层 0.4.23 会被合并进 +-- 沙箱需求后查无映射(version 当 ref 直取 → clone 失败)。0.4.23 为上游 +-- 仓库清理/测试收编批,消费面无功能差异;待 libjhasm 发内联含 0.4.23 的 +-- 版本后再同步上移。 +add_requires("libjbc 0.4.22") add_requires("libjstr 0.0.3") add_requires("libjzip 0.0.3") add_requires("libgarena 0.0.3") @@ -116,6 +121,9 @@ add_requireconfs("libjclassfile.libjstr.libca", {version = LUIOX_LIBCA_ add_requireconfs("libjstd.libca", {version = LUIOX_LIBCA_VERSION, configs = {modules = LUIOX_LIBCA_MODULES}}) add_requireconfs("libjdk_finder.libca", {version = LUIOX_LIBCA_VERSION, configs = {modules = LUIOX_LIBCA_MODULES}}) add_requireconfs("libjhasm.libca", {version = LUIOX_LIBCA_VERSION, configs = {modules = LUIOX_LIBCA_MODULES}}) +-- libjhasm 后端分支 dep(on_load 动态加挂)显式钉定:与顶层同一 (版本, 配置) +-- 归并单实例,且防沙箱内解析漂移到内联定义外的版本。 +add_requireconfs("libjhasm.libjbc", {version = "0.4.22"}) -- i18n/resources 构建规则内嵌(.lang add_files 规则,包安装产物不带规则)。 includes("xmake/libca_rules.lua")