il2cpp-dump-analyzer-mcp
Version:
Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games
386 lines • 14.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenericParser = void 0;
/**
* Parser for IL2CPP generic types and their instantiations
*/
class GenericParser {
/**
* Parse a generic type from class information
*/
parseGenericType(classInfo) {
// Check if this is a generic type
const genericMatch = classInfo.name.match(/^([^<]+)<(.+)>$/);
if (!genericMatch) {
return null;
}
const baseName = genericMatch[1];
const typeParametersStr = genericMatch[2];
// Parse type parameters
const genericParameters = this.parseGenericParameters(typeParametersStr, classInfo.body);
// Parse generic instantiations from comments
const instantiations = this.parseGenericInstantiations(classInfo.body);
// Parse inheritance
const baseClass = this.parseBaseClass(classInfo.inheritance);
const interfaces = this.parseInterfaces(classInfo.inheritance);
// Parse fields and methods
const fields = this.parseFields(classInfo.body);
const methods = this.parseMethods(classInfo.body);
return {
name: baseName,
namespace: this.extractNamespace(classInfo.declaration),
fullName: classInfo.name,
typeDefIndex: classInfo.typeDefIndex,
isNested: classInfo.name.includes('.'),
parentType: this.extractParentType(classInfo.name),
isCompilerGenerated: this.isCompilerGenerated(classInfo.attributes),
accessModifier: classInfo.accessModifier,
attributes: classInfo.attributes,
genericParameters,
isGenericDefinition: true,
isGenericInstance: false,
instantiations,
baseClass,
interfaces,
fields,
methods
};
}
/**
* Parse generic parameters from type parameter string
*/
parseGenericParameters(parametersStr, classBody) {
const parameters = [];
const paramNames = parametersStr.split(',').map(p => p.trim());
paramNames.forEach((paramName, index) => {
const constraints = this.extractConstraints(paramName, classBody);
const variance = this.extractVariance(paramName, classBody);
parameters.push({
name: paramName,
position: index,
constraints,
variance,
hasReferenceTypeConstraint: constraints.includes('class'),
hasValueTypeConstraint: constraints.includes('struct'),
hasDefaultConstructorConstraint: constraints.includes('new()')
});
});
return parameters;
}
/**
* Parse generic instantiations from method comments
*/
parseGenericInstantiations(classBody) {
const instantiations = [];
// Look for GenericInstMethod comments
const instantiationRegex = /\/\*\s*GenericInstMethod\s*:\s*([\s\S]*?)\*\//g;
let match;
while ((match = instantiationRegex.exec(classBody)) !== null) {
const content = match[1];
const lines = content.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('|-RVA:')) {
const instMatch = trimmed.match(/\|-RVA:\s*(0x[0-9A-F]+)\s+Offset:\s*(0x[0-9A-F]+)\s+VA:\s*(0x[0-9A-F]+)\s*\|\s*-([^<]+)<([^>]+)>/);
if (instMatch) {
const rva = instMatch[1];
const offset = instMatch[2];
const virtualAddress = instMatch[3];
const methodName = instMatch[4];
const typeArgumentsStr = instMatch[5];
const typeArguments = typeArgumentsStr.split(',').map(t => t.trim());
instantiations.push({
typeArguments,
rva,
offset,
virtualAddress,
methodName
});
}
}
}
}
return instantiations;
}
/**
* Extract constraints for a generic parameter
*/
extractConstraints(paramName, classBody) {
// For now, return empty array as constraints are not explicitly shown in IL2CPP dumps
// In a full implementation, this would analyze the class body for constraint usage
return [];
}
/**
* Extract variance for a generic parameter
*/
extractVariance(paramName, classBody) {
// For now, return 'none' as variance is not explicitly shown in IL2CPP dumps
// In a full implementation, this would analyze the parameter usage
return 'none';
}
/**
* 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 fields from class body
*/
parseFields(classBody) {
const fields = [];
const lines = classBody.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 class body
*/
parseMethods(classBody) {
const methods = [];
const lines = classBody.split('\n');
let i = 0;
while (i < lines.length) {
const line = lines[i].trim();
// Look for method declarations
const methodMatch = this.matchMethodDeclaration(line);
if (methodMatch) {
const method = this.parseMethodFromMatch(methodMatch, lines, i);
if (method) {
methods.push(method);
}
}
i++;
}
return methods;
}
/**
* Match method declarations including generic methods
*/
matchMethodDeclaration(line) {
// Enhanced regex to handle generic methods
const methodRegex = /^\s*((?:\[.*?\]\s*)*)?\s*(public|private|protected|internal)?\s*(static)?\s*(virtual)?\s*(override)?\s*(abstract)?\s*([^\s\(]+)\s+([^\s\(]+)\s*\(([^\)]*)\)\s*\{\s*\}(?:\s*\/\/.*)?$/;
return line.match(methodRegex);
}
/**
* Parse method from regex match
*/
parseMethodFromMatch(match, lines, lineIndex) {
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 attributes = this.parseAttributes(attributesStr);
const parameters = this.parseParameters(parametersStr);
// Extract RVA and offset from previous line if available
let rva = '';
let offset = '';
let slot = '';
if (lineIndex > 0) {
const prevLine = lines[lineIndex - 1].trim();
const rvaMatch = prevLine.match(/RVA:\s*(0x[0-9A-F]+|(-1))\s+Offset:\s*(0x[0-9A-F]+|(-1))(?:\s+VA:\s*0x[0-9A-F]+)?(?:\s+Slot:\s*(\d+))?/);
if (rvaMatch) {
rva = rvaMatch[1];
offset = rvaMatch[3];
slot = rvaMatch[5] || '';
}
}
// Check for generic instantiations in following lines
const genericInstantiations = this.parseMethodGenericInstantiations(lines, lineIndex);
return {
name,
returnType,
parameters,
isPublic: accessModifier === 'public',
isStatic,
isVirtual,
isAbstract,
isOverride,
attributes,
rva,
offset,
slot: slot ? parseInt(slot, 10) : undefined,
isGeneric: this.isGenericType(returnType) || parameters.some(p => this.isGenericType(p.type)),
genericInstantiations: genericInstantiations.length > 0 ? genericInstantiations : undefined
};
}
/**
* Parse generic instantiations for a specific method
*/
parseMethodGenericInstantiations(lines, startIndex) {
const instantiations = [];
let i = startIndex + 1;
// Look for GenericInstMethod comment block
while (i < lines.length) {
const line = lines[i].trim();
if (line.startsWith('/*') && line.includes('GenericInstMethod')) {
// Found start of generic instantiation block
i++;
while (i < lines.length && !lines[i].includes('*/')) {
const instLine = lines[i].trim();
if (instLine.startsWith('|-RVA:')) {
const instMatch = instLine.match(/\|-RVA:\s*(0x[0-9A-F]+)\s+Offset:\s*(0x[0-9A-F]+)\s+VA:\s*(0x[0-9A-F]+)\s*\|\s*-([^<]+)<([^>]+)>/);
if (instMatch) {
const rva = instMatch[1];
const offset = instMatch[2];
const virtualAddress = instMatch[3];
const methodName = instMatch[4];
const typeArgumentsStr = instMatch[5];
const typeArguments = typeArgumentsStr.split(',').map(t => t.trim());
instantiations.push({
typeArguments,
rva,
offset,
virtualAddress,
methodName
});
}
}
i++;
}
break;
}
else if (line === '' || line.startsWith('//')) {
// Skip empty lines and comments
i++;
}
else {
// Hit another declaration, stop looking
break;
}
}
return instantiations;
}
/**
* 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;
}
/**
* 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;
}
/**
* Check if a type is generic
*/
isGenericType(type) {
return type.includes('<') && type.includes('>');
}
/**
* Extract namespace from declaration
*/
extractNamespace(declaration) {
// For now, return empty string as namespace is tracked separately
return '';
}
/**
* Extract parent type from nested generic type name
*/
extractParentType(fullName) {
const dotIndex = fullName.lastIndexOf('.');
if (dotIndex > 0) {
return fullName.substring(0, dotIndex);
}
return undefined;
}
/**
* Check if the type is compiler generated
*/
isCompilerGenerated(attributes) {
return attributes.some(attr => attr.includes('CompilerGenerated'));
}
}
exports.GenericParser = GenericParser;
//# sourceMappingURL=generic-parser.js.map