Initial commit: AIGame Unity client with URP, HybridCLR, YooAsset and tooling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-09-21 14:21:07 +08:00
co-authored by Cursor
commit f3f66ab063
931 changed files with 112366 additions and 0 deletions
@@ -0,0 +1,106 @@
#nullable enable
using System;
using System.Text;
namespace IlInterpreter.Interpreter
{
// Debug tool: render a lowered method's IR (slot types, token/string tables, decoded ops) as text
// — the missing "see what the lowerer produced" view for chasing slot-typing/marshalling bugs.
// Internal, for tests and probe harnesses. Op widths come from OpWidthForCoalesce (never drifts).
partial class ScriptInterpreter
{
/// <summary>Disassemble one loaded method by simple name (first match). Null if not loaded/found.</summary>
internal string? DisassembleMethod(string name)
{
if (_parsed == null || !_parsed.ByName.TryGetValue(name, out var m)) return null;
return DisassembleParsed(m);
}
/// <summary>Disassemble every loaded method (declaration order by token).</summary>
internal string DisassembleAll()
{
var sb = new StringBuilder();
if (_parsed == null) return "(no script loaded)";
foreach (var m in _parsed.ByToken.Values)
sb.Append(DisassembleParsed(m)).Append('\n');
return sb.ToString();
}
string DisassembleParsed(ParsedMethod m)
{
var sb = new StringBuilder();
sb.Append($"=== {m.Name} (static={m.IsStatic}, args={m.ArgCount}, locals={m.LocalCount}, ret={m.ReturnSType}, argSTypes=[{string.Join(",", m.ArgSTypes)}]");
var lo = m.Lowered;
if (lo == null)
{
sb.Append($") NOT LOWERED: {m.LoweringSkipReason ?? "(no reason)"}\n");
return sb.ToString();
}
sb.Append($", frameSize={lo.FrameSize})\n");
// Slot type table (slot -> SType), compact.
sb.Append(" slots: ");
for (int s = 0; s < lo.SlotTypes.Length; s++)
sb.Append($"{s}:{lo.SlotTypes[s]} ");
sb.Append('\n');
if (lo.Strings.Length > 0)
{
sb.Append(" strings: ");
for (int i = 0; i < lo.Strings.Length; i++)
sb.Append($"[{i}]=\"{lo.Strings[i]}\" ");
sb.Append('\n');
}
// Instruction stream.
var ir = lo.Ir;
int ip = 0;
while (ip < ir.Length)
{
var op = (Op)ir[ip];
int w = IrLowerer.OpWidthForCoalesce(op, ir, ip);
if (w <= 0) { sb.Append($" {ip,4}: <bad width for {op}={(uint)ir[ip]}>\n"); break; }
int il = ip < lo.IrToIlOffset.Length ? lo.IrToIlOffset[ip] : -1;
sb.Append($" {ip,4} (IL 0x{il:X4}): {op,-16}");
for (int k = 1; k < w; k++) sb.Append($" {(int)ir[ip + k]}");
sb.Append(Annotate(op, ir, ip, w, lo));
sb.Append('\n');
ip += w;
}
return sb.ToString();
}
// Human-readable suffix for the operands we can resolve: the ldstr literal, the target of a
// host/script call, and the field behind a ld/stsfld/ld/stfld.
string Annotate(Op op, uint[] ir, int ip, int w, LoweredMethod lo)
{
switch (op)
{
case Op.ldstr:
// [op, dst, strIdx]
int si = (int)ir[ip + 2];
return si < lo.Strings.Length ? $" ; dst=s{(int)ir[ip + 1]} \"{lo.Strings[si]}\"" : " ; <bad str idx>";
case Op.call_host:
// [op, dst, recvSlot, tokIdx, argc, args...]
return $" ; dst=s{(int)ir[ip + 1]} recv=s{(int)ir[ip + 2]} -> {TokName(lo, (int)ir[ip + 3])}";
case Op.call_script:
case Op.newobj_script:
case Op.newobj_host:
// [op, dst, tokIdx, argc, args...]
return $" ; dst=s{(int)ir[ip + 1]} -> {TokName(lo, (int)ir[ip + 2])}";
case Op.ldsfld_i4: case Op.ldsfld_r4: case Op.ldsfld_o:
case Op.stsfld_i4: case Op.stsfld_r4: case Op.stsfld_o:
return $" ; {TokName(lo, (int)ir[ip + 2])}";
default:
return "";
}
}
string TokName(LoweredMethod lo, int tokIdx)
{
if (_parsed == null || lo.Tokens == null || (uint)tokIdx >= (uint)lo.Tokens.Length) return $"tok#{tokIdx}";
int tok = lo.Tokens[tokIdx];
return _parsed.TokenNames.TryGetValue(tok, out var n) ? n : $"tok 0x{tok:X8}";
}
}
}