il2cpp-dump-analyzer-mcp
Version:
Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games
337 lines • 12.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NestedTypeParser = void 0;
/**
* Parser for IL2CPP nested types including compiler-generated types
*/
class NestedTypeParser {
/**
* Parse nested types from class body
*/
parseNestedTypes(classBody, parentClassName, parentNamespace) {
const nestedTypes = [];
const lines = classBody.split('\n');
let i = 0;
while (i < lines.length) {
const line = lines[i].trim();
// Look for nested type declarations
const nestedMatch = this.matchNestedTypeDeclaration(line);
if (nestedMatch) {
const nestedTypeInfo = this.extractNestedTypeInfo(lines, i, parentClassName, parentNamespace);
if (nestedTypeInfo) {
nestedTypes.push(nestedTypeInfo.nestedType);
i = nestedTypeInfo.endLine;
}
else {
i++;
}
}
else {
i++;
}
}
return nestedTypes;
}
/**
* Match nested type declarations
*/
matchNestedTypeDeclaration(line) {
// Pattern for nested types: access_modifier [sealed] [static] type_kind ParentClass.NestedClass
const nestedRegex = /^((?:\[.*?\]\s*)*)?\s*(public|private|internal|protected)?\s*(sealed|static)?\s*(class|struct|enum|interface)\s+([^:\s]+\.[^:\s]+)(?:\s*:\s*([^{\/]+))?(?:\s*\/\/\s*TypeDefIndex:\s*(\d+))?/;
return line.match(nestedRegex);
}
/**
* Extract nested type information including body
*/
extractNestedTypeInfo(lines, startIndex, parentClassName, parentNamespace) {
const line = lines[startIndex].trim();
const match = this.matchNestedTypeDeclaration(line);
if (!match)
return null;
const attributesStr = match[1] || '';
const accessModifier = match[2] || 'private';
const modifiers = match[3] || '';
const typeKind = match[4];
const fullName = match[5];
const inheritance = match[6] || '';
const typeDefIndex = parseInt(match[7] || '0');
// Extract nested name from full name
const nestedName = this.extractNestedName(fullName, parentClassName);
if (!nestedName)
return null;
// Find the type body
const bodyInfo = this.extractTypeBody(lines, startIndex);
if (!bodyInfo)
return null;
// Parse attributes
const attributes = this.parseAttributes(attributesStr);
// Determine if compiler generated
const isCompilerGenerated = this.isCompilerGenerated(attributes, nestedName);
const compilerGeneratedType = isCompilerGenerated ? this.identifyCompilerGeneratedType(nestedName) : undefined;
// Parse inheritance
const baseClass = this.parseBaseClass(inheritance);
const interfaces = this.parseInterfaces(inheritance);
// Parse fields and methods
const fields = this.parseFields(bodyInfo.body);
const methods = this.parseMethods(bodyInfo.body);
const nestedType = {
name: nestedName,
namespace: parentNamespace,
fullName,
typeDefIndex,
isNested: true,
parentType: parentClassName,
isCompilerGenerated,
accessModifier: accessModifier,
attributes,
nestedLevel: this.calculateNestingLevel(fullName),
typeKind,
compilerGeneratedType,
fields,
methods,
baseClass,
interfaces
};
return {
nestedType,
endLine: bodyInfo.endLine
};
}
/**
* Extract nested type name from full name
*/
extractNestedName(fullName, parentClass) {
if (fullName.startsWith(parentClass + '.')) {
return fullName.substring(parentClass.length + 1);
}
// Handle cases where the parent class name might be different
const dotIndex = fullName.lastIndexOf('.');
if (dotIndex > 0) {
return fullName.substring(dotIndex + 1);
}
return null;
}
/**
* Extract type body (everything between braces)
*/
extractTypeBody(lines, startIndex) {
let braceCount = 0;
let endLine = startIndex;
// Find the opening brace
while (endLine < lines.length && !lines[endLine].includes('{')) {
endLine++;
}
if (endLine >= lines.length)
return null;
braceCount = 1;
endLine++;
// Find the matching closing brace
while (endLine < lines.length && braceCount > 0) {
const currentLine = lines[endLine];
braceCount += (currentLine.match(/{/g) || []).length;
braceCount -= (currentLine.match(/}/g) || []).length;
endLine++;
}
if (braceCount !== 0)
return null;
const body = lines.slice(startIndex, endLine).join('\n');
return { body, endLine };
}
/**
* Calculate nesting level based on dots in full name
*/
calculateNestingLevel(fullName) {
return (fullName.match(/\./g) || []).length;
}
/**
* Check if type is compiler generated
*/
isCompilerGenerated(attributes, name) {
// Check for CompilerGenerated attribute
if (attributes.some(attr => attr.includes('CompilerGenerated'))) {
return true;
}
// Check for compiler-generated naming patterns
return name.includes('<>') ||
name.includes('__') ||
/^[a-z]{2,3}$/.test(name) || // e.g., 'by', 'ca'
name.includes('DisplayClass') ||
name.includes('AnonymousType');
}
/**
* Identify the type of compiler-generated class
*/
identifyCompilerGeneratedType(name) {
if (name.includes('<>c'))
return 'anonymous_method_container';
if (name.includes('DisplayClass'))
return 'closure_class';
if (name.includes('<>f__AnonymousType'))
return 'anonymous_type';
if (/^[a-z]{2,3}$/.test(name))
return 'lambda_container'; // e.g., 'by', 'ca'
if (name.includes('__'))
return 'compiler_helper';
return 'unknown_generated';
}
/**
* Parse base class from inheritance string
*/
parseBaseClass(inheritance) {
if (!inheritance)
return undefined;
const types = inheritance.split(',').map(t => t.trim());
const firstType = types[0];
if (firstType && !this.isInterface(firstType)) {
return firstType;
}
return undefined;
}
/**
* Parse interfaces from inheritance string
*/
parseInterfaces(inheritance) {
if (!inheritance)
return [];
const types = inheritance.split(',').map(t => t.trim());
const interfaces = [];
// If the first type is a base class, start from index 1
const startIndex = this.parseBaseClass(inheritance) ? 1 : 0;
for (let i = startIndex; i < types.length; i++) {
if (this.isInterface(types[i])) {
interfaces.push(types[i]);
}
}
return interfaces;
}
/**
* Check if a type name represents an interface
*/
isInterface(typeName) {
return typeName.startsWith('I') &&
typeName.length > 1 &&
typeName[1] === typeName[1].toUpperCase();
}
/**
* Parse attributes from attribute string
*/
parseAttributes(attributesStr) {
if (!attributesStr)
return [];
const attributes = [];
const attrRegex = /\[(.*?)\]/g;
let match;
while ((match = attrRegex.exec(attributesStr)) !== null) {
const attrContent = match[1].trim();
attributes.push(attrContent);
}
return attributes;
}
/**
* Parse fields from type body
*/
parseFields(typeBody) {
const fields = [];
const lines = typeBody.split('\n');
const fieldRegex = /^\s*((?:\[.*?\]\s*)*)?\s*(public|private|protected|internal)?\s*(static)?\s*(readonly)?\s*([^\s]+)\s+([^\s;]+);(?:\s*\/\/\s*([0-9A-Fx]+))?/;
for (const line of lines) {
const match = line.match(fieldRegex);
if (match) {
const attributesStr = match[1] || '';
const accessModifier = match[2] || 'private';
const isStatic = !!match[3];
const isReadOnly = !!match[4];
const type = match[5];
const name = match[6];
const offset = match[7] || '';
const attributes = this.parseAttributes(attributesStr);
fields.push({
name,
type,
isPublic: accessModifier === 'public',
isStatic,
isReadOnly,
attributes,
offset,
isGeneric: this.isGenericType(type)
});
}
}
return fields;
}
/**
* Parse methods from type body
*/
parseMethods(typeBody) {
const methods = [];
const lines = typeBody.split('\n');
const methodRegex = /^\s*((?:\[.*?\]\s*)*)?\s*(public|private|protected|internal)?\s*(static)?\s*(virtual)?\s*(override)?\s*(abstract)?\s*([^\s\(]+)\s+([^\s\(]+)\s*\(([^\)]*)\);(?:\s*\/\/\s*RVA:\s*([0-9A-Fx]+)\s*Offset:\s*([0-9A-Fx]+))?/;
for (const line of lines) {
const match = line.match(methodRegex);
if (match) {
const attributesStr = match[1] || '';
const accessModifier = match[2] || 'private';
const isStatic = !!match[3];
const isVirtual = !!match[4];
const isOverride = !!match[5];
const isAbstract = !!match[6];
const returnType = match[7];
const name = match[8];
const parametersStr = match[9] || '';
const rva = match[10] || '';
const offset = match[11] || '';
const attributes = this.parseAttributes(attributesStr);
const parameters = this.parseParameters(parametersStr);
methods.push({
name,
returnType,
parameters,
isPublic: accessModifier === 'public',
isStatic,
isVirtual,
isAbstract,
isOverride,
attributes,
rva,
offset,
isGeneric: this.isGenericType(returnType) || parameters.some(p => this.isGenericType(p.type))
});
}
}
return methods;
}
/**
* Parse method parameters
*/
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;
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
*/
isGenericType(type) {
return type.includes('<') && type.includes('>');
}
}
exports.NestedTypeParser = NestedTypeParser;
//# sourceMappingURL=nested-type-parser.js.map