UNPKG

il2cpp-dump-analyzer-mcp

Version:

Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games

439 lines 20.3 kB
"use strict"; /** * @fileoverview Analyze Type Compatibility MCP Tool * * Provides comprehensive analysis of IL2CPP type compatibility including: * - Type assignability analysis and inheritance-based compatibility * - Interface implementation compatibility checking * - Generic type compatibility and constraint validation * - Conversion path analysis and implicit/explicit conversion detection * * This tool implements the type compatibility analysis functionality from TypeAnalyzer * as an MCP tool following established patterns and TFD methodology. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.analyzeTypeCompatibilitySchema = exports.AnalyzeTypeCompatibilityTool = void 0; exports.createAnalyzeTypeCompatibilityTool = createAnalyzeTypeCompatibilityTool; const zod_1 = require("zod"); const base_tool_handler_1 = require("../base-tool-handler"); const mcp_response_formatter_1 = require("../../utils/mcp-response-formatter"); /** * Zod schema for analyze type compatibility parameters */ const AnalyzeTypeCompatibilitySchema = zod_1.z.object({ from_type: zod_1.z.string().optional().describe('Source type for compatibility analysis (required for specific analysis)'), to_type: zod_1.z.string().optional().describe('Target type for compatibility analysis (required for specific analysis)'), include_conversion_paths: zod_1.z.boolean().default(true).describe('Whether to include conversion path analysis'), include_implicit_conversions: zod_1.z.boolean().default(true).describe('Whether to include implicit conversion analysis') }).refine(data => { // If one type is specified, both must be specified if (data.from_type && !data.to_type) return false; if (!data.from_type && data.to_type) return false; return true; }, { message: "Both from_type and to_type must be specified together, or neither for matrix analysis" }); /** * Analyze Type Compatibility MCP Tool Implementation * * Analyzes IL2CPP type compatibility and conversion relationships using vector store search. * Provides comprehensive compatibility analysis with assignability rules and conversion paths. */ class AnalyzeTypeCompatibilityTool extends base_tool_handler_1.BaseAnalysisToolHandler { constructor(context) { super({ name: 'analyze_type_compatibility', description: 'Analyze type compatibility, assignability rules, and conversion paths in IL2CPP dumps', enableParameterValidation: true, enableResponseFormatting: true }, context); } /** * Validate input parameters using Zod schema */ async validateParameters(params) { const errors = []; const warnings = []; const adjustedValues = {}; // Validate type pair requirement if (params.from_type && !params.to_type) { errors.push('to_type is required when from_type is specified'); } if (!params.from_type && params.to_type) { errors.push('from_type is required when to_type is specified'); } // Validate type names if provided if (params.from_type && typeof params.from_type !== 'string') { errors.push('from_type must be a string'); } if (params.to_type && typeof params.to_type !== 'string') { errors.push('to_type must be a string'); } // Set defaults for boolean parameters if (params.include_conversion_paths === undefined) { adjustedValues.include_conversion_paths = true; } if (params.include_implicit_conversions === undefined) { adjustedValues.include_implicit_conversions = true; } return { isValid: errors.length === 0, errors, warnings, adjustedValues }; } /** * Execute type compatibility analysis */ async executeCore(params) { return await this.performAnalysis(async () => { this.context.logger.debug('Starting type compatibility analysis', { params }); if (params.from_type && params.to_type) { // Specific type pair analysis return await this.analyzeSpecificTypeCompatibility(params); } else { // Matrix analysis for all types return await this.analyzeTypeCompatibilityMatrix(params); } }); } /** * Analyze compatibility between two specific types */ async analyzeSpecificTypeCompatibility(params) { const fromType = params.from_type; const toType = params.to_type; // Search for both types const fromTypeResults = await this.context.vectorStore.searchWithFilter(fromType, { type: ['class', 'interface', 'struct'] }, 1); const toTypeResults = await this.context.vectorStore.searchWithFilter(toType, { type: ['class', 'interface', 'struct'] }, 1); if (fromTypeResults.length === 0) { throw new Error(`Source type '${fromType}' not found in IL2CPP dump`); } if (toTypeResults.length === 0) { throw new Error(`Target type '${toType}' not found in IL2CPP dump`); } const fromTypeDoc = fromTypeResults[0]; const toTypeDoc = toTypeResults[0]; // Get all types for comprehensive analysis const allTypes = await this.context.vectorStore.searchWithFilter('', { type: ['class', 'interface', 'struct'] }, 1000); // Analyze compatibility const compatibility = this.analyzeTypeCompatibility(fromTypeDoc, toTypeDoc, allTypes, params); this.context.logger.debug('Specific type compatibility analysis completed', { fromType, toType, isCompatible: compatibility.isCompatible, compatibilityType: compatibility.compatibilityType }); return { fromType, toType, isCompatible: compatibility.isCompatible, compatibilityType: compatibility.compatibilityType, assignabilityRule: compatibility.assignabilityRule, conversionPath: compatibility.conversionPath, confidence: compatibility.confidence, analysisMetadata: { analysisType: 'specific', includeConversionPaths: params.include_conversion_paths ?? true, includeImplicitConversions: params.include_implicit_conversions ?? true, timestamp: new Date().toISOString(), totalTypesAnalyzed: 2, totalCompatibilityChecks: 1 } }; } /** * Analyze compatibility matrix for all types */ async analyzeTypeCompatibilityMatrix(params) { // Get all types const allTypes = await this.context.vectorStore.searchWithFilter('', { type: ['class', 'interface', 'struct'] }, 1000); if (allTypes.length === 0) { throw new Error('No types found in IL2CPP dump for compatibility analysis'); } this.context.logger.debug(`Found ${allTypes.length} types for compatibility matrix analysis`); const compatibilityMatrix = []; let totalChecks = 0; // Analyze compatibility between all type pairs (limited to avoid performance issues) const maxTypes = Math.min(allTypes.length, 20); // Limit to 20 types for matrix analysis const typesToAnalyze = allTypes.slice(0, maxTypes); for (let i = 0; i < typesToAnalyze.length; i++) { for (let j = 0; j < typesToAnalyze.length; j++) { if (i !== j) { // Don't analyze self-compatibility const fromTypeDoc = typesToAnalyze[i]; const toTypeDoc = typesToAnalyze[j]; const compatibility = this.analyzeTypeCompatibility(fromTypeDoc, toTypeDoc, allTypes, params); compatibilityMatrix.push({ fromType: `${fromTypeDoc.metadata.namespace}.${fromTypeDoc.metadata.name}`, toType: `${toTypeDoc.metadata.namespace}.${toTypeDoc.metadata.name}`, compatibility }); totalChecks++; } } } this.context.logger.debug('Type compatibility matrix analysis completed', { totalTypes: typesToAnalyze.length, totalChecks, compatiblePairs: compatibilityMatrix.filter(entry => entry.compatibility.isCompatible).length }); return { compatibilityMatrix, analysisMetadata: { analysisType: 'matrix', includeConversionPaths: params.include_conversion_paths ?? true, includeImplicitConversions: params.include_implicit_conversions ?? true, timestamp: new Date().toISOString(), totalTypesAnalyzed: typesToAnalyze.length, totalCompatibilityChecks: totalChecks } }; } /** * Analyze compatibility between two type documents */ analyzeTypeCompatibility(fromTypeDoc, toTypeDoc, allTypes, params) { const fromFullName = `${fromTypeDoc.metadata.namespace}.${fromTypeDoc.metadata.name}`; const toFullName = `${toTypeDoc.metadata.namespace}.${toTypeDoc.metadata.name}`; // Check direct assignability (inheritance) if (this.isAssignableViaInheritance(fromTypeDoc, toTypeDoc, allTypes)) { return { fromType: fromFullName, toType: toFullName, isCompatible: true, compatibilityType: 'assignable', assignabilityRule: { fromType: fromFullName, toType: toFullName, rule: 'inheritance_assignability', conditions: ['fromType inherits from toType'] }, confidence: 0.95 }; } // Check interface implementation if (this.isAssignableViaInterface(fromTypeDoc, toTypeDoc)) { return { fromType: fromFullName, toType: toFullName, isCompatible: true, compatibilityType: 'assignable', assignabilityRule: { fromType: fromFullName, toType: toFullName, rule: 'interface_assignability', conditions: ['fromType implements toType interface'] }, confidence: 0.90 }; } // Check generic compatibility if (this.isGenericCompatible(fromTypeDoc, toTypeDoc)) { return { fromType: fromFullName, toType: toFullName, isCompatible: true, compatibilityType: 'convertible', conversionPath: { fromType: fromFullName, toType: toFullName, path: [fromFullName, toFullName], conversionType: 'explicit' }, confidence: 0.75 }; } // Check built-in conversions if requested if (params.include_conversion_paths) { const conversionPath = this.findBuiltInConversionPath(fromTypeDoc, toTypeDoc, params.include_implicit_conversions); if (conversionPath) { return { fromType: fromFullName, toType: toFullName, isCompatible: true, compatibilityType: 'convertible', conversionPath, confidence: conversionPath.conversionType === 'implicit' ? 0.85 : 0.70 }; } } // Types are incompatible return { fromType: fromFullName, toType: toFullName, isCompatible: false, compatibilityType: 'incompatible', confidence: 0.95 }; } /** * Check if fromType is assignable to toType via inheritance */ isAssignableViaInheritance(fromTypeDoc, toTypeDoc, allTypes) { const toTypeName = toTypeDoc.metadata.name; const toFullName = `${toTypeDoc.metadata.namespace}.${toTypeDoc.metadata.name}`; // Create type lookup map const typeMap = new Map(); allTypes.forEach(doc => { const fullName = `${doc.metadata.namespace}.${doc.metadata.name}`; typeMap.set(fullName, doc); typeMap.set(doc.metadata.name, doc); // Also map by simple name }); // Traverse inheritance chain let currentType = fromTypeDoc; const visited = new Set(); while (currentType && currentType.metadata.baseClass) { const currentFullName = `${currentType.metadata.namespace}.${currentType.metadata.name}`; if (visited.has(currentFullName)) { break; // Circular inheritance detected } visited.add(currentFullName); const baseClass = currentType.metadata.baseClass; // Check if base class matches target type if (baseClass === toTypeName || baseClass === toFullName) { return true; } // Find base class document const baseClassDoc = typeMap.get(baseClass) || typeMap.get(baseClass.split('.').pop() || ''); if (!baseClassDoc) { break; } currentType = baseClassDoc; } return false; } /** * Check if fromType is assignable to toType via interface implementation */ isAssignableViaInterface(fromTypeDoc, toTypeDoc) { if (toTypeDoc.metadata.type !== 'interface') { return false; } const toTypeName = toTypeDoc.metadata.name; const toFullName = `${toTypeDoc.metadata.namespace}.${toTypeDoc.metadata.name}`; const interfaces = fromTypeDoc.metadata.interfaces || []; return interfaces.some((interfaceName) => interfaceName === toTypeName || interfaceName === toFullName || interfaceName.endsWith(`.${toTypeName}`)); } /** * Check if types are compatible via generic type relationships */ isGenericCompatible(fromTypeDoc, toTypeDoc) { // Check if both types are generic or generic instantiations const fromGeneric = fromTypeDoc.metadata.genericParameters || fromTypeDoc.metadata.genericInstantiation; const toGeneric = toTypeDoc.metadata.genericParameters || toTypeDoc.metadata.genericInstantiation; if (!fromGeneric || !toGeneric) { return false; } // Simple generic compatibility check // In a real implementation, this would be much more sophisticated const fromBaseName = fromTypeDoc.metadata.genericInstantiation?.baseType || fromTypeDoc.metadata.name; const toBaseName = toTypeDoc.metadata.genericInstantiation?.baseType || toTypeDoc.metadata.name; return fromBaseName === toBaseName; } /** * Find built-in conversion path between types */ findBuiltInConversionPath(fromTypeDoc, toTypeDoc, includeImplicit = true) { const fromTypeName = fromTypeDoc.metadata.name; const toTypeName = toTypeDoc.metadata.name; const fromFullName = `${fromTypeDoc.metadata.namespace}.${fromTypeDoc.metadata.name}`; const toFullName = `${toTypeDoc.metadata.namespace}.${toTypeDoc.metadata.name}`; // Define built-in conversion rules const implicitConversions = new Map([ ['byte', ['short', 'ushort', 'int', 'uint', 'long', 'ulong', 'float', 'double', 'decimal']], ['sbyte', ['short', 'int', 'long', 'float', 'double', 'decimal']], ['short', ['int', 'long', 'float', 'double', 'decimal']], ['ushort', ['int', 'uint', 'long', 'ulong', 'float', 'double', 'decimal']], ['int', ['long', 'float', 'double', 'decimal']], ['uint', ['long', 'ulong', 'float', 'double', 'decimal']], ['long', ['float', 'double', 'decimal']], ['ulong', ['float', 'double', 'decimal']], ['char', ['ushort', 'int', 'uint', 'long', 'ulong', 'float', 'double', 'decimal']], ['float', ['double']] ]); const explicitConversions = new Map([ ['double', ['float', 'decimal', 'long', 'ulong', 'int', 'uint', 'short', 'ushort', 'byte', 'sbyte', 'char']], ['float', ['decimal', 'long', 'ulong', 'int', 'uint', 'short', 'ushort', 'byte', 'sbyte', 'char']], ['decimal', ['double', 'float', 'long', 'ulong', 'int', 'uint', 'short', 'ushort', 'byte', 'sbyte', 'char']], ['long', ['int', 'uint', 'short', 'ushort', 'byte', 'sbyte', 'char']], ['ulong', ['long', 'int', 'uint', 'short', 'ushort', 'byte', 'sbyte', 'char']], ['int', ['uint', 'short', 'ushort', 'byte', 'sbyte', 'char']], ['uint', ['int', 'short', 'ushort', 'byte', 'sbyte', 'char']], ['short', ['ushort', 'byte', 'sbyte', 'char']], ['ushort', ['short', 'byte', 'sbyte', 'char']], ['byte', ['sbyte']], ['sbyte', ['byte', 'char']] ]); // Check implicit conversions if (includeImplicit && implicitConversions.has(fromTypeName)) { const targets = implicitConversions.get(fromTypeName); if (targets.includes(toTypeName)) { return { fromType: fromFullName, toType: toFullName, path: [fromFullName, toFullName], conversionType: 'implicit' }; } } // Check explicit conversions if (explicitConversions.has(fromTypeName)) { const targets = explicitConversions.get(fromTypeName); if (targets.includes(toTypeName)) { return { fromType: fromFullName, toType: toFullName, path: [fromFullName, toFullName], conversionType: 'explicit' }; } } return null; } /** * Format type compatibility analysis results */ formatResponse(result, warnings = []) { let response = mcp_response_formatter_1.MCPResponseFormatter.formatAnalysisResults(result, this.config.name, result.analysisMetadata, Date.now() - this.startTime); if (warnings.length > 0) { response = mcp_response_formatter_1.MCPResponseFormatter.addWarnings(response, warnings); } return response; } } exports.AnalyzeTypeCompatibilityTool = AnalyzeTypeCompatibilityTool; /** * Zod schema for analyze type compatibility tool parameters */ exports.analyzeTypeCompatibilitySchema = zod_1.z.object({ from_type: zod_1.z.string().optional().describe("Source type for compatibility analysis (required for specific analysis)"), to_type: zod_1.z.string().optional().describe("Target type for compatibility analysis (required for specific analysis)"), include_conversion_paths: zod_1.z.boolean().optional().default(true).describe("Include conversion path analysis"), include_implicit_conversions: zod_1.z.boolean().optional().default(true).describe("Include implicit conversion analysis") }).refine(data => { // If one type is specified, both must be specified if (data.from_type && !data.to_type) return false; if (!data.from_type && data.to_type) return false; return true; }, { message: "Both from_type and to_type must be specified together, or neither for matrix analysis" }); /** * Factory function to create and register the analyze type compatibility tool */ function createAnalyzeTypeCompatibilityTool(server, context) { const handler = new AnalyzeTypeCompatibilityTool(context); server.tool("analyze_type_compatibility", exports.analyzeTypeCompatibilitySchema, async (params) => { return await handler.execute(params); }); return handler; } //# sourceMappingURL=analyze-type-compatibility-tool.js.map