il2cpp-dump-analyzer-mcp
Version:
Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games
290 lines • 11.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DelegateParser = void 0;
/**
* Parser for IL2CPP delegate types
* Handles delegates that inherit from MulticastDelegate or Delegate
*/
class DelegateParser {
/**
* Parse a delegate from class declaration and body
*/
parseDelegate(classInfo) {
// 1. Verify inheritance from MulticastDelegate or Delegate
if (!this.isDelegateType(classInfo.inheritance)) {
return null;
}
// 2. Extract delegate name and parent class
const { name, parentClass } = this.parseDelegateName(classInfo.name);
// 3. Parse the Invoke method to get signature
const invokeMethod = this.parseInvokeMethod(classInfo.body);
if (!invokeMethod) {
return null;
}
// 4. Parse constructor and async methods
const constructorMethod = this.parseConstructorMethod(classInfo.body);
const beginInvokeMethod = this.parseBeginInvokeMethod(classInfo.body);
const endInvokeMethod = this.parseEndInvokeMethod(classInfo.body);
if (!constructorMethod) {
return null;
}
return {
name,
namespace: this.extractNamespace(classInfo.declaration),
fullName: classInfo.name,
typeDefIndex: classInfo.typeDefIndex,
isNested: !!parentClass,
parentType: parentClass,
isCompilerGenerated: this.isCompilerGenerated(classInfo.attributes),
accessModifier: classInfo.accessModifier,
attributes: classInfo.attributes,
returnType: invokeMethod.returnType,
parameters: invokeMethod.parameters,
isMulticast: classInfo.inheritance.includes('MulticastDelegate'),
invokeMethod,
constructorMethod,
beginInvokeMethod: beginInvokeMethod || undefined,
endInvokeMethod: endInvokeMethod || undefined
};
}
/**
* Check if the inheritance indicates a delegate type
*/
isDelegateType(inheritance) {
return inheritance.includes('MulticastDelegate') ||
inheritance.includes('Delegate');
}
/**
* Parse delegate name and extract parent class if nested
*/
parseDelegateName(fullName) {
// Handle nested delegates like "EventMgr.OnGesture"
const dotIndex = fullName.lastIndexOf('.');
if (dotIndex > 0) {
return {
name: fullName.substring(dotIndex + 1),
parentClass: fullName.substring(0, dotIndex)
};
}
return { name: fullName };
}
/**
* Parse the Invoke method which defines the delegate signature
*/
parseInvokeMethod(classBody) {
const lines = classBody.split('\n');
// Look for the Invoke method
const invokeRegex = /^\s*(?:\/\/[^\n]*\n)?\s*public\s+virtual\s+([^\s]+)\s+Invoke\s*\(([^)]*)\)\s*\{\s*\}(?:\s*\/\/.*)?$/m;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.includes('public virtual') && line.includes('Invoke(')) {
// Extract method signature
const match = line.match(/public\s+virtual\s+([^\s]+)\s+Invoke\s*\(([^)]*)\)/);
if (match) {
const returnType = match[1];
const parametersStr = match[2];
// Parse parameters
const parameters = this.parseParameters(parametersStr);
// Extract RVA and offset from previous line if available
let rva = '';
let offset = '';
let slot = '';
if (i > 0) {
const prevLine = lines[i - 1].trim();
const rvaMatch = prevLine.match(/RVA:\s*(0x[0-9A-F]+)\s+Offset:\s*(0x[0-9A-F]+)(?:\s+VA:\s*0x[0-9A-F]+)?(?:\s+Slot:\s*(\d+))?/);
if (rvaMatch) {
rva = rvaMatch[1];
offset = rvaMatch[2];
slot = rvaMatch[3] || '';
}
}
return {
name: 'Invoke',
returnType,
parameters,
isPublic: true,
isStatic: false,
isVirtual: true,
isAbstract: false,
isOverride: false,
attributes: [],
rva,
offset,
slot: slot ? parseInt(slot, 10) : undefined
};
}
}
}
return null;
}
/**
* Parse the constructor method
*/
parseConstructorMethod(classBody) {
const lines = classBody.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.includes('public void .ctor(object object, IntPtr method)')) {
// Extract RVA and offset from previous line if available
let rva = '';
let offset = '';
if (i > 0) {
const prevLine = lines[i - 1].trim();
const rvaMatch = prevLine.match(/RVA:\s*(0x[0-9A-F]+)\s+Offset:\s*(0x[0-9A-F]+)/);
if (rvaMatch) {
rva = rvaMatch[1];
offset = rvaMatch[2];
}
}
return {
name: '.ctor',
returnType: 'void',
parameters: [
{ name: 'object', type: 'object' },
{ name: 'method', type: 'IntPtr' }
],
isPublic: true,
isStatic: false,
isVirtual: false,
isAbstract: false,
isOverride: false,
attributes: [],
rva,
offset
};
}
}
return null;
}
/**
* Parse the BeginInvoke method
*/
parseBeginInvokeMethod(classBody) {
const lines = classBody.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.includes('public virtual IAsyncResult BeginInvoke(')) {
const match = line.match(/public\s+virtual\s+IAsyncResult\s+BeginInvoke\s*\(([^)]*)\)/);
if (match) {
const parametersStr = match[1];
const parameters = this.parseParameters(parametersStr);
// Extract RVA and offset
let rva = '';
let offset = '';
let slot = '';
if (i > 0) {
const prevLine = lines[i - 1].trim();
const rvaMatch = prevLine.match(/RVA:\s*(0x[0-9A-F]+)\s+Offset:\s*(0x[0-9A-F]+)(?:\s+VA:\s*0x[0-9A-F]+)?(?:\s+Slot:\s*(\d+))?/);
if (rvaMatch) {
rva = rvaMatch[1];
offset = rvaMatch[2];
slot = rvaMatch[3] || '';
}
}
return {
name: 'BeginInvoke',
returnType: 'IAsyncResult',
parameters,
isPublic: true,
isStatic: false,
isVirtual: true,
isAbstract: false,
isOverride: false,
attributes: [],
rva,
offset,
slot: slot ? parseInt(slot, 10) : undefined
};
}
}
}
return null;
}
/**
* Parse the EndInvoke method
*/
parseEndInvokeMethod(classBody) {
const lines = classBody.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.includes('public virtual void EndInvoke(IAsyncResult result)')) {
// Extract RVA and offset
let rva = '';
let offset = '';
let slot = '';
if (i > 0) {
const prevLine = lines[i - 1].trim();
const rvaMatch = prevLine.match(/RVA:\s*(0x[0-9A-F]+)\s+Offset:\s*(0x[0-9A-F]+)(?:\s+VA:\s*0x[0-9A-F]+)?(?:\s+Slot:\s*(\d+))?/);
if (rvaMatch) {
rva = rvaMatch[1];
offset = rvaMatch[2];
slot = rvaMatch[3] || '';
}
}
return {
name: 'EndInvoke',
returnType: 'void',
parameters: [{ name: 'result', type: 'IAsyncResult' }],
isPublic: true,
isStatic: false,
isVirtual: true,
isAbstract: false,
isOverride: false,
attributes: [],
rva,
offset,
slot: slot ? parseInt(slot, 10) : undefined
};
}
}
return null;
}
/**
* Parse method parameters from parameter string
*/
parseParameters(parametersStr) {
if (!parametersStr || parametersStr.trim() === '') {
return [];
}
const parameters = [];
const paramParts = parametersStr.split(',');
for (const part of paramParts) {
const trimmed = part.trim();
if (!trimmed)
continue;
// Split by last space to separate type and name
const lastSpaceIndex = trimmed.lastIndexOf(' ');
if (lastSpaceIndex !== -1) {
const type = trimmed.substring(0, lastSpaceIndex).trim();
const name = trimmed.substring(lastSpaceIndex + 1).trim();
parameters.push({
type,
name,
isGeneric: this.isGenericType(type)
});
}
}
return parameters;
}
/**
* Check if a type is generic (contains < and >)
*/
isGenericType(type) {
return type.includes('<') && type.includes('>');
}
/**
* Extract namespace from declaration
*/
extractNamespace(declaration) {
// For now, return empty string as namespace is tracked separately
return '';
}
/**
* Check if the type is compiler generated
*/
isCompilerGenerated(attributes) {
return attributes.some(attr => attr.includes('CompilerGenerated'));
}
}
exports.DelegateParser = DelegateParser;
//# sourceMappingURL=delegate-parser.js.map