Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions mjbase/src/mjbase/renamer/shadow_token.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 += ';';
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions mjbase/src/mjbase/renamer/shadow_token.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),不静默回落裸名。
Expand Down
120 changes: 120 additions & 0 deletions mjbase/test/unittest/renamer/shadow_token_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,4 +662,124 @@ 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);
}

// ── §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
12 changes: 10 additions & 2 deletions xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,19 @@ 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")
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}})
Expand All @@ -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")

Expand Down
Loading