diff --git a/mjbase/src/mjbase/renamer/shadow_token.cpp b/mjbase/src/mjbase/renamer/shadow_token.cpp index d135e0c..e92d6aa 100644 --- a/mjbase/src/mjbase/renamer/shadow_token.cpp +++ b/mjbase/src/mjbase/renamer/shadow_token.cpp @@ -85,7 +85,8 @@ void appendRepairAction(ShadowSlotError error, std::string& text) break; case ShadowSlotError::AmbiguousMember: text += "; supply the raw descriptor to disambiguate the overload " - "(descriptor token grammar lands later; pass the raw descriptor for now)"; + "(descriptor slots accept embedded class handles; " + "three-part member handles are future work)"; break; case ShadowSlotError::MissingSidecar: text += "; generate a shadow sidecar first (MCP shadow_generate, or `mjt shadow -i `)"; @@ -303,16 +304,13 @@ ShadowSlotResult resolveShadowSlot(const ShadowAliasTable& table, const std::string& input, const ShadowSlotQuery& query) { - if (query.position != ShadowSlotPosition::Name) { - // 未接线分支占位:descriptor / jasm body 标识槽文法未接,fail-loud - // 防意外提前使用;后续扩展在本收口内替换本分支,不开新入口。 - return failure(ShadowSlotError::MalformedHandle, - std::string(query.position == ShadowSlotPosition::Descriptor - ? "descriptor" - : "jasm-body") + - " slot does not accept shadow references yet (planned); " - "pass the raw form for now"); + if (query.position == ShadowSlotPosition::Descriptor) { + return resolveShadowDescriptor(table, input); + } + if (query.position == ShadowSlotPosition::JasmBody) { + return resolveShadowJasmBody(table, input); } + // 其余位置:Name(缺省);无未接线分支。 const auto parts = parseShadowRef(input); switch (parts.form) { @@ -350,6 +348,313 @@ ShadowSlotResult resolveShadowSlot(const ShadowAliasTable& table, return failure(ShadowSlotError::MalformedHandle, "unreachable reference form"); } + +ShadowSlotResult resolveShadowDescriptor(const ShadowAliasTable& table, + const std::string& input) +{ + ShadowSlotResult result; + std::string out; + std::size_t cursor = 0; + ca::usize expansions = 0; + + // 单遍词法走查 + 轻量描述符文法校验(垃圾入 fail-loud,不静默替换)。 + // 返回 true = 校验与展开完成(result.raw 就绪);false = 已填 Malformed + // 类失败。 + const auto emitTypeSegment = [&]() -> bool { + // 前置 '[' 已由调用方消费;此处 input[cursor] ∈ {基类型, 'L', 'V'?}。 + const char c = input[cursor]; + if (c == 'L') { + ++cursor; + const auto nameBegin = cursor; + while (cursor < input.size() && input[cursor] != ';') { + // 泛型签名标记:fail-loud(Sig 属性 v2 另批)。 + if (input[cursor] == '<' || input[cursor] == '>') { + result = failure(ShadowSlotError::MalformedHandle, + "generic signature marker '" + std::string(1, input[cursor]) + + "' inside descriptor " + quoted(input) + + " is not accepted by the descriptor slot (planned); " + "pass the erased descriptor"); + return false; + } + ++cursor; + } + if (cursor >= input.size()) { + result = failure(ShadowSlotError::MalformedHandle, + "unterminated object type segment in descriptor " + + quoted(input)); + return false; + } + const auto content = input.substr(nameBegin, cursor - nameBegin); + ++cursor; // ';' + const auto parts = parseShadowRef(content); + switch (parts.form) { + case ShadowRefForm::Raw: + out += 'L'; + out += content; + out += ';'; + break; + case ShadowRefForm::EscapedLiteral: + // 字面请求:剥 $ 后的段按普通类名输出(与 Name 槽剥皮语义一致)。 + out += 'L'; + out += parts.literal; + out += ';'; + break; + case ShadowRefForm::ClassRef: { + ShadowSlotResult entryResult; + const auto entry = + resolveShadowHandleEntry(table, std::string(content), entryResult); + if (!entry.has_value()) { + result = std::move(entryResult); + return false; + } + out += 'L'; + out += entry->rawName; + out += ';'; + ++expansions; + break; + } + case ShadowRefForm::MemberRef: + result = failure(ShadowSlotError::MalformedHandle, + "member reference " + quoted(content) + + " cannot appear in a type position of descriptor " + + quoted(input)); + return false; + case ShadowRefForm::Malformed: + result = failure(ShadowSlotError::MalformedHandle, + "malformed reference " + quoted(content) + + " inside descriptor " + quoted(input)); + return false; + } + return true; + } + // 基类型(含 V 的调用方约束在外面)。 + static constexpr std::string_view kPrimitives = "BCDFIJSZ"; + if (kPrimitives.find(c) == std::string_view::npos) { + result = failure(ShadowSlotError::MalformedHandle, + "byte " + std::string(1, c) + " at offset " + + std::to_string(cursor) + " does not fit a descriptor: " + + quoted(input)); + return false; + } + out += c; + ++cursor; + return true; + }; + + const auto parseFieldType = [&]() -> bool { + while (cursor < input.size() && input[cursor] == '[') { + out += '['; + ++cursor; + } + return emitTypeSegment(); + }; + + bool ok = false; + if (cursor < input.size() && input[cursor] == '(') { + // 方法描述符:( 参数类型* ) 返回类型(V 或字段类型)。 + out += '('; + ++cursor; + while (cursor < input.size() && input[cursor] != ')') { + if (!parseFieldType()) return result; + } + if (cursor >= input.size()) { + result = failure(ShadowSlotError::MalformedHandle, + "unterminated parameter list in descriptor " + quoted(input)); + return result; + } + out += ')'; + ++cursor; + if (cursor < input.size() && input[cursor] == 'V') { + out += 'V'; + ++cursor; + } + else { + ok = parseFieldType(); + if (!ok) return result; + } + } + else { + ok = parseFieldType(); + if (!ok) return result; + } + if (cursor != input.size()) { + result = failure(ShadowSlotError::MalformedHandle, + "trailing bytes after descriptor " + quoted(input) + " at offset " + + std::to_string(cursor)); + return result; + } + result.error = ShadowSlotError::None; + result.raw = std::move(out); + result.fromHandle = expansions > 0; + return result; +} + + +ShadowSlotResult resolveShadowJasmBody(const ShadowAliasTable& table, + const std::string& input) +{ + ShadowSlotResult result; + if (input.empty()) { + result.raw = input; + result.error = ShadowSlotError::None; + return result; + } + + // ── 尾随描述符切分 ── + std::string refPart; + std::string descPart; // 括号内(方法)或尾 token(字段);空 = 无 + std::string separator; // 引用部与描述符之间的原样分隔(" (" / " ") + bool hasDesc = false; + if (input.back() == ')') { + const auto paren = input.rfind(" ("); + if (paren == std::string::npos || paren + 2 >= input.size() - 1) { + return failure(ShadowSlotError::MalformedHandle, + "operand '" + brief(input) + + "' ends with ')' but has no ' (desc)' group"); + } + refPart = input.substr(0, paren); + descPart = input.substr(paren + 2, input.size() - paren - 3); + separator = " ("; + hasDesc = true; + } + else { + const auto space = input.rfind(' '); + if (space != std::string::npos && space + 1 < input.size()) { + refPart = input.substr(0, space); + descPart = input.substr(space + 1); + separator = " "; + hasDesc = true; + } + else { + refPart = input; + } + } + + // ── 引用部解析 ── + // 产物:resolvedRef(替换后引用部);expansions 计数。 + // 分派序:整体 parseShadowRef(ClassRef/MemberRef/EscapedLiteral 直达); + // Malformed 且带 '${' 前缀 → 复合拆分(首个 '}' 前 = owner handle,余部 + // ".name");其余 Raw(owner.name 复合或纯裸名零漂移直通)。 + std::string resolvedRef; + ca::usize expansions = 0; + + // name 段:裸名直通;嵌套 MemberRef handle 展开并核验与 raw owner 自洽。 + // 失败时已填 result,返回 false。 + const auto resolveNamePart = [&](const std::string& namePart, + const std::string& rawOwner, + std::string& out) -> bool { + if (namePart.rfind("${", 0) != 0) { + out = namePart; + return true; + } + if (parseShadowRef(namePart).form != ShadowRefForm::MemberRef) { + result = failure(ShadowSlotError::MalformedHandle, + "malformed member handle in jasm operand: " + + quoted(namePart)); + return false; + } + ShadowSlotResult entryResult; + const auto entry = resolveShadowHandleEntry(table, namePart, entryResult); + if (!entry.has_value()) { + result = std::move(entryResult); + return false; + } + if (entry->owner != rawOwner) { + result = failure(ShadowSlotError::MalformedHandle, + "member handle owner " + brief(entry->owner) + + " conflicts with operand owner " + brief(rawOwner)); + return false; + } + out = entry->rawName; + ++expansions; + return true; + }; + + const auto parts = parseShadowRef(refPart); + if (parts.form == ShadowRefForm::ClassRef) { + ShadowSlotResult entryResult; + const auto entry = resolveShadowHandleEntry(table, refPart, entryResult); + if (!entry.has_value()) return entryResult; + resolvedRef = entry->rawName; + ++expansions; + } + else if (parts.form == ShadowRefForm::MemberRef) { + // 整体成员句柄:展开为 rawOwner.rawName 复合。 + ShadowSlotResult entryResult; + const auto entry = resolveShadowHandleEntry(table, refPart, entryResult); + if (!entry.has_value()) return entryResult; + resolvedRef = std::string(entry->owner) + "." + std::string(entry->rawName); + ++expansions; + } + else if (parts.form == ShadowRefForm::EscapedLiteral) { + resolvedRef = parts.literal; + } + else if (parts.form == ShadowRefForm::Malformed && refPart.rfind("${", 0) == 0) { + // 复合拆分:首个 '}' 前为 owner handle(须 ClassRef 形状);表查询 + // 错误原样直传——unknown-handle 不得被复合语境吞成 malformed。 + const auto close = refPart.find('}'); + if (close == std::string::npos) { + return failure(ShadowSlotError::MalformedHandle, + "malformed reference " + quoted(refPart) + + " in jasm operand (no closing brace)"); + } + const auto handlePart = refPart.substr(0, close + 1); + const auto rest = refPart.substr(close + 1); + if (rest.empty() || rest[0] != '.') { + return failure(ShadowSlotError::MalformedHandle, + "malformed reference " + quoted(refPart) + + " in jasm operand (expected .name after handle)"); + } + if (parseShadowRef(handlePart).form != ShadowRefForm::ClassRef) { + return failure(ShadowSlotError::MalformedHandle, + "malformed owner handle " + quoted(handlePart) + + " in jasm operand"); + } + ShadowSlotResult ownerResult; + const auto ownerEntry = + resolveShadowHandleEntry(table, handlePart, ownerResult); + if (!ownerEntry.has_value()) return ownerResult; + const std::string rawOwner(ownerEntry->rawName); + std::string resolvedName; + if (!resolveNamePart(rest.substr(1), rawOwner, resolvedName)) return result; + resolvedRef = rawOwner + "." + resolvedName; + ++expansions; + } + else if (parts.form == ShadowRefForm::Malformed) { + return failure(ShadowSlotError::MalformedHandle, + "malformed reference " + quoted(refPart) + " in jasm operand"); + } + else { + // Raw:复合 owner.name(name 可嵌 MemberRef handle)或纯裸名。 + const auto dot = refPart.find('.'); + if (dot == std::string::npos) { + resolvedRef = refPart; // 类操作数裸名:零漂移直通 + } + else { + const auto ownerPart = refPart.substr(0, dot); + const auto namePart = refPart.substr(dot + 1); + std::string resolvedName; + if (!resolveNamePart(namePart, ownerPart, resolvedName)) return result; + resolvedRef = ownerPart + "." + resolvedName; + } + } + + // ── 描述符部展开 ── + std::string resolvedDesc; + if (hasDesc) { + const auto descResult = resolveShadowDescriptor(table, descPart); + if (descResult.error != ShadowSlotError::None) return descResult; + resolvedDesc = descResult.raw; + if (descResult.fromHandle) ++expansions; + } + + result.error = ShadowSlotError::None; + result.raw = resolvedRef + separator + (hasDesc ? resolvedDesc : "") + + (separator == " (" ? ")" : ""); + result.fromHandle = expansions > 0; + return result; +} + std::string shadowRefHandleOf(const ShadowAliasTable& table, const ShadowAliasEntry& entry) { const std::string token(entry.token8); diff --git a/mjbase/src/mjbase/renamer/shadow_token.hpp b/mjbase/src/mjbase/renamer/shadow_token.hpp index b2dcb00..3772a91 100644 --- a/mjbase/src/mjbase/renamer/shadow_token.hpp +++ b/mjbase/src/mjbase/renamer/shadow_token.hpp @@ -12,8 +12,7 @@ /// ambiguous-member,各自带固定修复动作;缺 sidecar 时任何引用 /// 形状 → 硬报错(missing-sidecar),不静默回落裸名。 /// -/// 边界:仅 Name 槽实现;Descriptor/JasmBody 分支未接线(传入即 fail-loud, -/// 不做文法)。实修:查表命中面一律按值/optional 交付—— +/// 边界:Name/Descriptor/JasmBody 槽已实现。实修:查表命中面一律按值/optional 交付—— /// findClassByHandle 返回指向 findByShadowName 临时 vector 内部的指针(悬垂 /// UB,mjt 仓 9e72e9f 已修),本移植自始不带该形状。 #pragma once @@ -33,9 +32,10 @@ enum class ShadowSlotPosition /// 整串参数槽:class/owner/name/class_uri/uri 等表意"类或成员引用"的 /// 参数。整串 = Ref 或 raw 二选一(无子串扫描)。 Name, - /// 方法/字段描述符、Sig 属性 v2:词法走查 L…; 完整段。未接线。 + /// 方法/字段描述符:词法走查 L…; 完整段,类型位展开(泛型签名 + /// fail-loud,Sig 属性 v2 另批)。 Descriptor, - /// JASM body 指令操作数中的内部名/描述符。未接线。 + /// JASM body 指令操作数(引用型):类名 / owner.name 复合 + 尾随描述符。 JasmBody, }; @@ -148,12 +148,53 @@ struct ShadowSlotResult /// 零漂移:无表可核即原样通过)。 /// - 缺 sidecar(空表)+ 引用形状(ClassRef/MemberRef/Malformed)→ /// MissingSidecar 硬报错,不静默回落裸名。 -/// - Descriptor/JasmBody 位置:未接线,fail-loud(MalformedHandle + 指引), -/// 防意外提前使用。 +/// - Descriptor 位置:走 resolveShadowDescriptor(见下)。 +/// - JasmBody 位置:走 resolveShadowJasmBody(见下)。 ShadowSlotResult resolveShadowSlot(const ShadowAliasTable& table, const std::string& input, const ShadowSlotQuery& query = {}); +/// @brief Descriptor 槽(§3 位置三分之描述符行):字段/方法描述符内 +/// `L…;` 段的类型位展开。 +/// +/// 文法(纯描述符,泛型签名不在内): +/// - 词法走查 `(`/`)`/`[`/基类型(B C D F I J S Z V)逐字节直通; +/// - `L…;` 段:段内容按 parseShadowRef 分形—— +/// Raw 恒等直通(描述符类型位任指任意类型,零漂移不核表, +/// query.verifyRaw 在本槽不适用);EscapedLiteral 剥 `$` 后直通; +/// ClassRef 查表展开为真实内部名(同 resolveShadowHandleEntry 四分类, +/// UnknownHandle/MissingSidecar 原样上抛);MemberRef → MalformedHandle +/// (类型位不收成员引用);Malformed → MalformedHandle; +/// - 其余字节(含 `<`/`>`)不合描述符文法 → MalformedHandle(泛型签名 +/// fail-loud,v2 另批;不静默替换产生垃圾输出)。 +/// +/// 结果:raw = 替换后的完整描述符;fromHandle = 发生过 ≥1 次展开; +/// descriptor/kind 不承载(类型位没有成员 desc 语义)。 +ShadowSlotResult resolveShadowDescriptor(const ShadowAliasTable& table, + const std::string& input); + +/// @brief JasmBody 槽(§3 位置三分之操作数行):引用型指令操作数展开。 +/// +/// 输入 = 单条指令的引用型操作数(助记符之后;iinc/数字等非引用操作数由 +/// 调用方不分派本槽)。文法(对齐 rewriteJasmForDisplay 的显示形态): +/// +/// - 尾随描述符切分:以 ")" 结尾 → 取最后一个 " (" 之前的引用部 + 括号内 +/// 方法描述符;否则取最后一个空格之后的字段描述符;无空格 = 纯引用部 +/// (类操作数 new/checkcast/instanceof/anewarray/ldc 类常量); +/// - 引用部:整体 ClassRef → 类内部名;整体 MemberRef → `rawOwner.rawName` +/// 复合;`OWNER.RAWNAME` 复合(OWNER = ClassRef 或裸内部名)→ owner 展开/ +/// 恒等;纯裸名 → 恒等直通(显示文本的 shadow 名如 `Class_1.m_1` 不含 +/// `\${` 形状,按 Raw 零漂移直通——shadow→raw 的通路是回显工具,不在本槽); +/// 形状冲突(owner 不同的嵌套 MemberRef 等)→ MalformedHandle; +/// - 描述符部:经 resolveShadowDescriptor 展开(类 handle 可内嵌); +/// - query.verifyRaw/query.member/query.descHint 在本槽不适用(raw 零漂移: +/// java/lang/System.out 等表外目标恒等放行)。 +/// +/// 结果:raw = 替换后的完整操作数;fromHandle = 发生过 ≥1 次展开; +/// descriptor/kind 不承载。 +ShadowSlotResult resolveShadowJasmBody(const ShadowAliasTable& table, + const std::string& input); + /// @brief 引用句柄(ClassRef/MemberRef)全量核验 → 命中条目(Ref 分支的条目面, /// 供回显工具 handle→条目反查;与 resolveShadowSlot 的 Ref 分支同判同错)。 /// diff --git a/mjbase/test/unittest/renamer/shadow_token_test.cpp b/mjbase/test/unittest/renamer/shadow_token_test.cpp index 6ec2458..59a5847 100644 --- a/mjbase/test/unittest/renamer/shadow_token_test.cpp +++ b/mjbase/test/unittest/renamer/shadow_token_test.cpp @@ -24,7 +24,8 @@ namespace { /// 手工表(全部自造名/号): /// Class_1 = a/b/Alpha(token 11111111),方法 m_1 = fly ()V(token 22222222), -/// 字段 f_1 = weight I(token 33333333);Class_2 = a/b/Beta(token 44444444)。 +/// 字段 f_1 = weight I(token 33333333);Class_2 = a/b/Beta(token 44444444), +/// 方法 m_2 = hunt (I)V(token 55555555)。 ShadowAliasTable makeTable() { ShadowAliasTable table; @@ -32,6 +33,7 @@ ShadowAliasTable makeTable() table.addMethod("a/b/Alpha", "()V", "fly", "m_1", "22222222"); table.addField("a/b/Alpha", "I", "weight", "f_1", "33333333"); table.addClass("a/b/Beta", "Class_2", "44444444"); + table.addMethod("a/b/Beta", "(I)V", "hunt", "m_2", "55555555"); return table; } @@ -203,19 +205,23 @@ TEST(MjShadowTokenTest, EmptyTableAnyReferenceShapeIsMissingSidecarHardError) } } -TEST(MjShadowTokenTest, NonNamePositionsFailLoudUntilWired) +TEST(MjShadowTokenTest, NonNamePositionsDispatch) { - // Descriptor/JasmBody 未接线:fail-loud(MalformedHandle + 指引),不做文法。 + // Descriptor 已接线:裸 handle 不是描述符 → 文法违约(MalformedHandle), + // 不再是「未接线」占位拒绝。 ShadowSlotQuery query; query.position = ShadowSlotPosition::Descriptor; const auto desc = resolveShadowSlot(makeTable(), kClassRef, query); EXPECT_EQ(ShadowSlotError::MalformedHandle, desc.error); - EXPECT_NE(std::string::npos, text(desc).find("does not accept shadow references")); + EXPECT_EQ(std::string::npos, text(desc).find("does not accept shadow references")); + // JasmBody 已接线:裸 handle(类操作数位)按 ClassRef 展开,不再是 + // 「未接线」占位拒绝。 query.position = ShadowSlotPosition::JasmBody; const auto jasm = resolveShadowSlot(makeTable(), kClassRef, query); - EXPECT_EQ(ShadowSlotError::MalformedHandle, jasm.error); - EXPECT_NE(std::string::npos, text(jasm).find("does not accept shadow references")); + EXPECT_EQ(ShadowSlotError::None, jasm.error); + EXPECT_EQ(jasm.raw, "a/b/Alpha"); + EXPECT_TRUE(jasm.fromHandle); } // ───────────────────────── §6 错误四分类(Ref 面) ───────────────────────── @@ -342,7 +348,7 @@ TEST(MjShadowTokenTest, StringConstantTextNeverClassifiesAsReference) TEST(MjShadowTokenTest, HandleIssuanceCoversClassAndMemberEntries) { const auto table = makeTable(); - ASSERT_EQ(4U, table.size()); + ASSERT_EQ(5U, table.size()); std::string classHandle; std::string memberHandle; for (const auto& entry : table.entries()) { @@ -350,7 +356,7 @@ TEST(MjShadowTokenTest, HandleIssuanceCoversClassAndMemberEntries) if (entry.kind == ShadowKind::Class && entry.rawName == "a/b/Alpha") { classHandle = handle; } - if (entry.kind == ShadowKind::Method) { + if (entry.kind == ShadowKind::Method && entry.rawName == "fly") { memberHandle = handle; EXPECT_EQ("${Class_1.11111111.m_1.22222222}", handle); } @@ -384,4 +390,276 @@ TEST(MjShadowTokenTest, HandleIssuanceSkipsTokenlessAndManualNames) } } + +// ── Descriptor 槽(A 批 A1:类型位展开)────────────────────────── + +namespace { + +/// Descriptor 槽快捷入口。 +ShadowSlotResult resolveDesc(const ShadowAliasTable& table, const std::string& input) +{ + ShadowSlotQuery query; + query.position = ShadowSlotPosition::Descriptor; + return resolveShadowSlot(table, input, query); +} + +} // namespace + +TEST(MjShadowTokenTest, DescriptorPlainIdentityZeroDrift) +{ + const auto table = makeTable(); + // 无 token 的纯描述符恒等直通(java/lang/String 不在表内也零漂移)。 + const auto r1 = resolveDesc(table, "(Ljava/lang/String;I)V"); + ASSERT_EQ(r1.error, ShadowSlotError::None); + EXPECT_EQ(r1.raw, "(Ljava/lang/String;I)V"); + EXPECT_FALSE(r1.fromHandle); + + const auto r2 = resolveDesc(table, "Ljava/lang/Object;"); + ASSERT_EQ(r2.error, ShadowSlotError::None); + EXPECT_EQ(r2.raw, "Ljava/lang/Object;"); + EXPECT_FALSE(r2.fromHandle); +} + +TEST(MjShadowTokenTest, DescriptorClassRefExpandsInTypePosition) +{ + const auto table = makeTable(); + const auto r = resolveDesc(table, "(L" + std::string(kClassRef) + ";I)V"); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "(La/b/Alpha;I)V"); + EXPECT_TRUE(r.fromHandle); +} + +TEST(MjShadowTokenTest, DescriptorArrayAndFieldForms) +{ + const auto table = makeTable(); + // 字段描述符:数组 + handle 类型位。 + const auto r = resolveDesc(table, "[L" + std::string(kClassRef) + ";"); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "[La/b/Alpha;"); + EXPECT_TRUE(r.fromHandle); + + // 双重数组 + 裸类型恒等。 + const auto r2 = resolveDesc(table, "[[La/b/Beta;"); + ASSERT_EQ(r2.error, ShadowSlotError::None) << text(r2); + EXPECT_EQ(r2.raw, "[[La/b/Beta;"); + EXPECT_FALSE(r2.fromHandle); + + // 方法多参数:数组参数 + 基类型参数(连续字段类型)。 + const auto r3 = resolveDesc(table, "([L" + std::string(kClassRef) + ";[I[I)V"); + ASSERT_EQ(r3.error, ShadowSlotError::None) << text(r3); + EXPECT_EQ(r3.raw, "([La/b/Alpha;[I[I)V"); + EXPECT_TRUE(r3.fromHandle); + + // 单字段描述符带尾随第二类型 = 文法违约(字段只有一个类型)。 + EXPECT_EQ(resolveDesc(table, "[L" + std::string(kClassRef) + ";[I").error, + ShadowSlotError::MalformedHandle); +} + +TEST(MjShadowTokenTest, DescriptorMultiExpansionAndDispatchViaSlot) +{ + const auto table = makeTable(); + const std::string input = + "(L" + std::string(kClassRef) + ";L" + std::string(kClassRef) + ";)L" + + std::string(kClassRef) + ";"; + const auto r = resolveDesc(table, input); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "(La/b/Alpha;La/b/Alpha;)La/b/Alpha;"); + EXPECT_TRUE(r.fromHandle); +} + +TEST(MjShadowTokenTest, DescriptorMissingSidecarHardFails) +{ + const ShadowAliasTable empty; + const auto r = resolveDesc(empty, "(L" + std::string(kClassRef) + ";)V"); + EXPECT_EQ(r.error, ShadowSlotError::MissingSidecar); +} + +TEST(MjShadowTokenTest, DescriptorUnknownHandlePropagates) +{ + const auto table = makeTable(); + // token 错 → unknown-handle 原样上抛。 + const auto r = resolveDesc(table, "(L${Class_1.deadbeef};)V"); + EXPECT_EQ(r.error, ShadowSlotError::UnknownHandle); +} + +TEST(MjShadowTokenTest, DescriptorMemberRefRejectedInTypePosition) +{ + const auto table = makeTable(); + const auto r = resolveDesc(table, "(L" + std::string(kMemberRef) + ";)V"); + EXPECT_EQ(r.error, ShadowSlotError::MalformedHandle); + EXPECT_NE(r.message.find("type position"), std::string::npos); +} + +TEST(MjShadowTokenTest, DescriptorGenericSignatureFailsLoud) +{ + const auto table = makeTable(); + // 泛型签名标记 fail-loud(Sig v2 另批),不静默替换。 + const auto r = resolveDesc(table, "(L" + std::string(kClassRef) + ";)V"); + EXPECT_EQ(r.error, ShadowSlotError::MalformedHandle); + EXPECT_NE(r.message.find("generic signature"), std::string::npos); +} + +TEST(MjShadowTokenTest, DescriptorGrammarGarbageFailsLoud) +{ + const auto table = makeTable(); + // 裸 handle 不是描述符。 + EXPECT_EQ(resolveDesc(table, kClassRef).error, ShadowSlotError::MalformedHandle); + // 尾随垃圾。 + EXPECT_EQ(resolveDesc(table, "(I)VX").error, ShadowSlotError::MalformedHandle); + // 未闭合参数表。 + EXPECT_EQ(resolveDesc(table, "(I").error, ShadowSlotError::MalformedHandle); + // V 不出现在字段描述符/参数位。 + EXPECT_EQ(resolveDesc(table, "VI").error, ShadowSlotError::MalformedHandle); + // 空串。 + EXPECT_EQ(resolveDesc(table, "").error, ShadowSlotError::MalformedHandle); + // 段内 Malformed 引用。 + EXPECT_EQ(resolveDesc(table, "(L${;I)V").error, ShadowSlotError::MalformedHandle); +} + +TEST(MjShadowTokenTest, DescriptorEscapedLiteralKeepsLiteralBraces) +{ + const auto table = makeTable(); + // $${…} = 字面请求:剥 $ 后的字面名即含 ${ 的名字本身(与 Name 槽 + // 剥皮语义一致——字面类名按原样输出,不查表)。 + const auto r = resolveDesc(table, "(L$${a/b/Weird};)V"); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "(L${a/b/Weird};)V"); + EXPECT_FALSE(r.fromHandle); +} + + +// ── JasmBody 槽(A 批 A2:引用型操作数展开)────────────────────── + +namespace { + +/// JasmBody 槽快捷入口。 +ShadowSlotResult resolveJasm(const ShadowAliasTable& table, const std::string& input) +{ + ShadowSlotQuery query; + query.position = ShadowSlotPosition::JasmBody; + return resolveShadowSlot(table, input, query); +} + +} // namespace + +TEST(MjShadowTokenTest, JasmBodyRawOperandsIdentityZeroDrift) +{ + const auto table = makeTable(); + // 显示文本的 shadow 名(Class_1.m_1,无 ${ 形状)按 Raw 零漂移直通—— + // shadow→raw 的通路是回显工具,不在本槽。 + const auto r1 = resolveJasm(table, "java/lang/System.out Ljava/io/PrintStream;"); + ASSERT_EQ(r1.error, ShadowSlotError::None) << text(r1); + EXPECT_EQ(r1.raw, "java/lang/System.out Ljava/io/PrintStream;"); + EXPECT_FALSE(r1.fromHandle); + + const auto r2 = resolveJasm(table, "Alpha.run ()V"); + ASSERT_EQ(r2.error, ShadowSlotError::None) << text(r2); + EXPECT_EQ(r2.raw, "Alpha.run ()V"); + + const auto r3 = resolveJasm(table, "a/b/Alpha"); + ASSERT_EQ(r3.error, ShadowSlotError::None) << text(r3); + EXPECT_EQ(r3.raw, "a/b/Alpha"); +} + +TEST(MjShadowTokenTest, JasmBodyClassHandleOperandExpands) +{ + const auto table = makeTable(); + // 类操作数(new/checkcast/ldc 类常量位):整体 ClassRef。 + const auto r = resolveJasm(table, kClassRef); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "a/b/Alpha"); + EXPECT_TRUE(r.fromHandle); +} + +TEST(MjShadowTokenTest, JasmBodyMemberHandleWithMethodDescExpands) +{ + const auto table = makeTable(); + // 整体 MemberRef + 方法描述符:复合 rawOwner.rawName + desc 直通。 + const auto r = resolveJasm(table, std::string(kMemberRef) + " ()V"); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "a/b/Alpha.fly ()V"); + EXPECT_TRUE(r.fromHandle); +} + +TEST(MjShadowTokenTest, JasmBodyOwnerHandleCompositeExpands) +{ + const auto table = makeTable(); + // owner = ClassRef handle + 裸成员名 + 字段描述符。 + const auto r = resolveJasm(table, std::string(kClassRef) + ".weight I"); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "a/b/Alpha.weight I"); + EXPECT_TRUE(r.fromHandle); +} + +TEST(MjShadowTokenTest, JasmBodyDescHandlesExpandViaDescriptorSlot) +{ + const auto table = makeTable(); + // 裸 owner.name + 描述符内嵌类 handle:desc 走 Descriptor 槽。 + const auto r = resolveJasm(table, "a/b/Alpha.run (L" + std::string(kClassRef) + ";)V"); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "a/b/Alpha.run (La/b/Alpha;)V"); + EXPECT_TRUE(r.fromHandle); +} + +TEST(MjShadowTokenTest, JasmBodyMemberHandleOwnerMismatchFailsLoud) +{ + const auto table = makeTable(); + // owner handle + 嵌套异 owner 的 MemberRef handle:owner 不自洽 → fail-loud。 + const auto r = + resolveJasm(table, "${Class_1.11111111}.${Class_2.44444444.m_2.55555555} ()V"); + EXPECT_EQ(r.error, ShadowSlotError::MalformedHandle); +} + +TEST(MjShadowTokenTest, JasmBodyOwnerHandleWithMemberHandleExpands) +{ + const auto table = makeTable(); + // owner handle + 同 owner 嵌套 MemberRef handle:自洽 → 展开。 + const auto r = + resolveJasm(table, std::string(kClassRef) + "." + std::string(kMemberRef) + " ()V"); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "a/b/Alpha.fly ()V"); + EXPECT_TRUE(r.fromHandle); +} + +TEST(MjShadowTokenTest, JasmBodyRawOwnerWithMemberHandleExpands) +{ + const auto table = makeTable(); + // 裸 raw owner + 嵌套 MemberRef handle(Raw 复合路径)。 + const auto r = resolveJasm(table, "a/b/Alpha." + std::string(kMemberRef) + " ()V"); + ASSERT_EQ(r.error, ShadowSlotError::None) << text(r); + EXPECT_EQ(r.raw, "a/b/Alpha.fly ()V"); + EXPECT_TRUE(r.fromHandle); +} + +TEST(MjShadowTokenTest, JasmBodyMissingSidecarHardFails) +{ + const ShadowAliasTable empty; + const auto r = resolveJasm(empty, std::string(kMemberRef) + " ()V"); + EXPECT_EQ(r.error, ShadowSlotError::MissingSidecar); +} + +TEST(MjShadowTokenTest, JasmBodyUnknownHandlePropagates) +{ + const auto table = makeTable(); + const auto r = resolveJasm(table, "${Class_1.deadbeef}.fly ()V"); + EXPECT_EQ(r.error, ShadowSlotError::UnknownHandle); +} + +TEST(MjShadowTokenTest, JasmBodyGrammarGarbageFailsLoud) +{ + const auto table = makeTable(); + // 以 ')' 结尾但无 ' (' 组。 + EXPECT_EQ(resolveJasm(table, "A.f)").error, ShadowSlotError::MalformedHandle); + // owner handle 残缺(无 '}' 收口)。 + EXPECT_EQ(resolveJasm(table, "${Class_1.fly ()V").error, ShadowSlotError::MalformedHandle); + // 段内 Malformed 引用。 + EXPECT_EQ(resolveJasm(table, "${x}.fly ()V").error, ShadowSlotError::MalformedHandle); + // 描述符文法垃圾(走 Descriptor 槽 fail-loud)。 + EXPECT_EQ(resolveJasm(table, "A.f (I)VX").error, ShadowSlotError::MalformedHandle); + // 空串恒等。 + const auto r = resolveJasm(table, ""); + ASSERT_EQ(r.error, ShadowSlotError::None); + EXPECT_EQ(r.raw, ""); +} + } // namespace mjbase::renamer