UNPKG

il2cpp-dump-analyzer-mcp

Version:

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

1,013 lines 44.2 kB
"use strict"; /** * @fileoverview TypeAnalyzer - Type System Analysis and Relationship Mapping * * Provides comprehensive analysis of IL2CPP type systems including: * - Inheritance hierarchy analysis and interface implementations * - Generic type relationship mapping and constraint analysis * - Type dependency graph creation and circular reference detection * - Type compatibility analysis and relationship validation * * This module builds upon the metadata extraction capabilities to provide * deep insights into type relationships and dependencies within IL2CPP dumps. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeAnalyzer = void 0; /** * TypeAnalyzer - Main class for type system analysis and relationship mapping * * Provides comprehensive analysis capabilities for IL2CPP type systems including * inheritance hierarchies, interface implementations, generic relationships, * dependency graphs, circular reference detection, and compatibility analysis. */ class TypeAnalyzer { constructor() { this.logger = console; } /** * Analyze inheritance hierarchies and interface implementations * * @param metadata - IL2CPP metadata containing type information * @returns Comprehensive inheritance hierarchy analysis */ async analyzeInheritanceHierarchies(metadata) { this.logger.debug('Starting inheritance hierarchy analysis'); try { const types = metadata.typeSystem.genericTypes; const hierarchies = []; const multipleInheritancePatterns = []; const orphanedTypes = []; // Build type lookup map for efficient access const typeMap = new Map(); for (const type of types) { const fullName = `${type.namespace}.${type.name}`; typeMap.set(fullName, type); } // Find root types (types with no base type or base type is System.Object) const rootTypes = types.filter(type => !type.baseType || type.baseType === 'System.Object' || !typeMap.has(type.baseType)); // Build hierarchies from root types for (const rootType of rootTypes) { const hierarchyTree = this.buildHierarchyTree(rootType, typeMap, 0); const flattenedDerived = this.flattenHierarchyTree(hierarchyTree); const hierarchy = { rootType: `${rootType.namespace}.${rootType.name}`, depth: this.calculateMaxDepth(hierarchyTree) + 1, // Add 1 to convert from depth index to level count derivedTypes: flattenedDerived, // Flatten to get all derived types totalTypes: this.countTypesInHierarchy(hierarchyTree) }; hierarchies.push(hierarchy); } // Detect multiple inheritance patterns - sort by complexity to get most complex first const typesWithMultipleInheritance = types.filter(type => type.interfaces.length > 1 || (type.baseType && type.interfaces.length > 0)); typesWithMultipleInheritance .sort((a, b) => { const aComplexity = (a.baseType ? 1 : 0) + a.interfaces.length; const bComplexity = (b.baseType ? 1 : 0) + b.interfaces.length; // If complexity is the same, prioritize types that inherit from other types in this list if (aComplexity === bComplexity) { const aInheritsFromListType = a.baseType && typesWithMultipleInheritance.some(t => `${t.namespace}.${t.name}` === a.baseType); const bInheritsFromListType = b.baseType && typesWithMultipleInheritance.some(t => `${t.namespace}.${t.name}` === b.baseType); if (aInheritsFromListType && !bInheritsFromListType) return -1; if (!aInheritsFromListType && bInheritsFromListType) return 1; // If still tied, sort by name for consistency return `${a.namespace}.${a.name}`.localeCompare(`${b.namespace}.${b.name}`); } return bComplexity - aComplexity; // Sort by complexity descending }) .forEach(type => { const pattern = this.analyzeMultipleInheritancePattern(type); multipleInheritancePatterns.push(pattern); }); // Find orphaned types const hierarchyTypes = new Set(); for (const hierarchy of hierarchies) { this.collectHierarchyTypesFromNodes(hierarchy.derivedTypes, hierarchyTypes); } for (const type of types) { const fullName = `${type.namespace}.${type.name}`; if (!hierarchyTypes.has(fullName)) { orphanedTypes.push(fullName); } } const maxDepth = Math.max(...hierarchies.map(h => h.depth), 0); this.logger.debug(`Inheritance hierarchy analysis completed: ${hierarchies.length} hierarchies, ${multipleInheritancePatterns.length} multiple inheritance patterns`); return { hierarchies, multipleInheritancePatterns, orphanedTypes, maxDepth, totalHierarchies: hierarchies.length }; } catch (error) { this.logger.error('Error during inheritance hierarchy analysis:', error); throw error; } } /** * Analyze interface implementations and their relationships * * @param metadata - IL2CPP metadata containing type information * @returns Comprehensive interface implementation analysis */ async analyzeInterfaceImplementations(metadata) { this.logger.debug('Starting interface implementation analysis'); try { const types = metadata.typeSystem.genericTypes; const implementations = []; const interfaceHierarchies = []; const implementationCoverage = new Map(); const orphanedInterfaces = []; // Identify interfaces and implementing types const interfaces = types.filter(type => type.name.startsWith('I') && type.name.length > 1); const implementingTypes = types.filter(type => type.interfaces.length > 0); // Analyze implementations for (const type of implementingTypes) { const implementation = { implementingType: `${type.namespace}.${type.name}`, namespace: type.namespace, interfaces: type.interfaces, implicitImplementations: type.interfaces.filter(iface => this.isImplicitImplementation(iface, type)), explicitImplementations: type.interfaces.filter(iface => !this.isImplicitImplementation(iface, type)) }; implementations.push(implementation); } // Build interface hierarchies - only for base interfaces (those that don't inherit from other interfaces) const baseInterfaces = interfaces.filter(iface => iface.interfaces.length === 0); for (const iface of baseInterfaces) { const hierarchy = this.buildInterfaceHierarchy(iface, types); // Only add hierarchies that have derived interfaces or implementing types if (hierarchy.derivedInterfaces.length > 0 || hierarchy.implementingTypes.length > 0) { interfaceHierarchies.push(hierarchy); } } // Calculate implementation coverage for (const iface of interfaces) { const fullName = `${iface.namespace}.${iface.name}`; const implementationCount = implementations.filter(impl => impl.interfaces.includes(fullName)).length; implementationCoverage.set(fullName, implementationCount); } // Find orphaned interfaces for (const iface of interfaces) { const fullName = `${iface.namespace}.${iface.name}`; if ((implementationCoverage.get(fullName) || 0) === 0) { orphanedInterfaces.push(fullName); } } this.logger.debug(`Interface implementation analysis completed: ${implementations.length} implementations, ${interfaceHierarchies.length} interface hierarchies`); return { implementations, interfaceHierarchies, implementationCoverage, orphanedInterfaces }; } catch (error) { this.logger.error('Error during interface implementation analysis:', error); throw error; } } /** * Map generic type relationships and constraints * * @param metadata - IL2CPP metadata containing type information * @returns Comprehensive generic type relationship analysis */ async mapGenericTypeRelationships(metadata) { this.logger.debug('Starting generic type relationship mapping'); try { const types = metadata.typeSystem.genericTypes; const genericRelationships = []; const constraintAnalysis = []; const instantiationPatterns = []; // Analyze generic types const genericDefinitions = types.filter(type => type.isGenericDefinition); const genericInstances = types.filter(type => type.isGenericInstance); // Build relationships for generic definitions for (const genericDef of genericDefinitions) { const relationship = this.analyzeGenericTypeRelationship(genericDef, genericInstances); genericRelationships.push(relationship); // Analyze constraints for (const param of genericDef.genericParameters) { const constraints = genericDef.constraints.filter(c => c.includes(param)); if (constraints.length > 0) { const analysis = this.analyzeConstraints(param, constraints); constraintAnalysis.push(analysis); } } } // Analyze instantiation patterns for (const genericDef of genericDefinitions) { const pattern = this.analyzeInstantiationPattern(genericDef, genericInstances); instantiationPatterns.push(pattern); } // Calculate complexity metrics const complexityMetrics = this.calculateGenericComplexityMetrics(genericDefinitions, genericInstances); this.logger.debug(`Generic type relationship mapping completed: ${genericRelationships.length} relationships, ${constraintAnalysis.length} constraint analyses`); return { genericRelationships, constraintAnalysis, instantiationPatterns, complexityMetrics }; } catch (error) { this.logger.error('Error during generic type relationship mapping:', error); throw error; } } /** * Create type dependency graph and circular reference detection * * @param metadata - IL2CPP metadata containing type information * @returns Comprehensive type dependency graph */ async createTypeDependencyGraph(metadata) { this.logger.debug('Starting type dependency graph creation'); try { const types = metadata.typeSystem.genericTypes; const nodes = []; const edges = []; const clusters = []; // Create dependency nodes for (const type of types) { const node = this.createDependencyNode(type, types); nodes.push(node); } // Create dependency edges for (const type of types) { const typeEdges = this.createDependencyEdges(type, types); edges.push(...typeEdges); } // Calculate metrics const metrics = this.calculateDependencyMetrics(nodes, edges); // Create type clusters const typeClusters = this.createTypeClusters(nodes, edges); clusters.push(...typeClusters); this.logger.debug(`Type dependency graph created: ${nodes.length} nodes, ${edges.length} edges, ${clusters.length} clusters`); return { nodes, edges, metrics, clusters }; } catch (error) { this.logger.error('Error during type dependency graph creation:', error); throw error; } } /** * Detect circular references in type relationships * * @param metadata - IL2CPP metadata containing type information * @returns Circular reference detection results */ async detectCircularReferences(metadata) { this.logger.debug('Starting circular reference detection'); try { const types = metadata.typeSystem.genericTypes; const circularReferences = []; const affectedTypes = []; const severityDistribution = new Map(); const resolutionSuggestions = []; // Build dependency graph for cycle detection const dependencyGraph = this.buildDependencyGraphForCycleDetection(types); // Detect cycles using DFS const cycles = this.detectCyclesUsingDFS(dependencyGraph); // Analyze each cycle for (const cycle of cycles) { const circularRef = this.analyzeCycle(cycle, types); circularReferences.push(circularRef); // Track affected types for (const typeName of cycle) { if (!affectedTypes.includes(typeName)) { affectedTypes.push(typeName); } } // Update severity distribution const currentCount = severityDistribution.get(circularRef.severity) || 0; severityDistribution.set(circularRef.severity, currentCount + 1); // Generate resolution suggestions const suggestion = this.generateResolutionSuggestion(circularRef); resolutionSuggestions.push(suggestion); } this.logger.debug(`Circular reference detection completed: ${circularReferences.length} cycles found, ${affectedTypes.length} affected types`); return { circularReferences, affectedTypes, severityDistribution, resolutionSuggestions }; } catch (error) { this.logger.error('Error during circular reference detection:', error); throw error; } } /** * Analyze type compatibility and assignability * * @param metadata - IL2CPP metadata containing type information * @returns Type compatibility analysis results */ async analyzeTypeCompatibility(metadata) { this.logger.debug('Starting type compatibility analysis'); try { const types = metadata.typeSystem.genericTypes; const compatibilityMatrix = new Map(); const assignabilityRules = []; const conversionPaths = []; const incompatibilityReasons = new Map(); // Analyze compatibility between all type pairs for (const fromType of types) { for (const toType of types) { const compatibility = this.analyzeTypeCompatibilityPair(fromType, toType, types); const key = `${fromType.namespace}.${fromType.name}->${toType.namespace}.${toType.name}`; compatibilityMatrix.set(key, compatibility.type); if (compatibility.type === 'assignable' && compatibility.rule) { assignabilityRules.push(compatibility.rule); } else if (compatibility.type === 'convertible' && compatibility.conversionPath) { conversionPaths.push(compatibility.conversionPath); } else if (compatibility.type === 'incompatible' && compatibility.reason) { incompatibilityReasons.set(key, compatibility.reason); } } } // Add some basic built-in type compatibility rules this.addBuiltInCompatibilityRules(compatibilityMatrix, types); this.logger.debug(`Type compatibility analysis completed: ${compatibilityMatrix.size} compatibility relationships analyzed`); return { compatibilityMatrix, assignabilityRules, conversionPaths, incompatibilityReasons }; } catch (error) { this.logger.error('Error during type compatibility analysis:', error); throw error; } } // Private helper methods implementation buildHierarchyTree(type, typeMap, depth) { const fullName = `${type.namespace}.${type.name}`; const node = { typeName: type.name, namespace: type.namespace, typeDefIndex: type.typeDefIndex, baseType: type.baseType, derivedTypes: [], depth, interfaces: type.interfaces }; // Find derived types for (const [typeName, typeData] of typeMap) { if (typeData.baseType === fullName) { const derivedNode = this.buildHierarchyTree(typeData, typeMap, depth + 1); node.derivedTypes.push(derivedNode); } } return node; } analyzeMultipleInheritancePattern(type) { const baseTypes = type.baseType ? [type.baseType] : []; const totalInheritance = baseTypes.length + type.interfaces.length; let complexity = 'low'; if (totalInheritance > 3) { complexity = 'high'; } else if (totalInheritance > 1) { complexity = 'medium'; } return { typeName: `${type.namespace}.${type.name}`, namespace: type.namespace, baseTypes, interfaces: type.interfaces, complexity }; } countTypesInHierarchy(hierarchy) { let count = 1; // Count the current node for (const derived of hierarchy.derivedTypes) { count += this.countTypesInHierarchy(derived); } return count; } collectHierarchyTypesFromNodes(nodes, typeSet) { for (const node of nodes) { const fullName = `${node.namespace}.${node.typeName}`; typeSet.add(fullName); this.collectHierarchyTypesFromNodes(node.derivedTypes, typeSet); } } flattenHierarchyTree(hierarchyTree) { const result = []; // Add all derived types recursively for (const derived of hierarchyTree.derivedTypes) { result.push(derived); result.push(...this.flattenHierarchyTree(derived)); } return result; } findTypeInHierarchy(hierarchyTree, typeName) { // Check if this node matches const fullName = `${hierarchyTree.namespace}.${hierarchyTree.typeName}`; if (fullName === typeName) { return true; } // Check derived types recursively for (const derived of hierarchyTree.derivedTypes) { if (this.findTypeInHierarchy(derived, typeName)) { return true; } } return false; } calculateMaxDepth(hierarchy) { let maxDepth = hierarchy.depth; for (const derived of hierarchy.derivedTypes) { const derivedDepth = this.calculateMaxDepth(derived); maxDepth = Math.max(maxDepth, derivedDepth); } return maxDepth; } collectHierarchyTypes(hierarchies, typeSet) { for (const hierarchy of hierarchies) { const fullName = `${hierarchy.namespace}.${hierarchy.typeName}`; typeSet.add(fullName); this.collectHierarchyTypes(hierarchy.derivedTypes, typeSet); } } isImplicitImplementation(interfaceName, type) { // Check if the interface is implemented through inheritance return type.baseType !== undefined && interfaceName.includes(type.baseType); } buildInterfaceHierarchy(iface, types) { const fullName = `${iface.namespace}.${iface.name}`; // Find derived interfaces (interfaces that inherit from this interface) const derivedInterfaces = types .filter(type => { const typeFullName = `${type.namespace}.${type.name}`; // Check if this interface inherits from the base interface const inheritsFromBase = type.interfaces.some(inheritedInterface => { // Handle both full names and short names return inheritedInterface === fullName || inheritedInterface === iface.name || `${type.namespace}.${inheritedInterface}` === fullName; }); return inheritsFromBase && type.name.startsWith('I') && typeFullName !== fullName; }) .map(type => `${type.namespace}.${type.name}`); // Find implementing types (non-interface types that implement this interface) const implementingTypes = types .filter(type => { const typeFullName = `${type.namespace}.${type.name}`; const implementsInterface = type.interfaces.some(implementedInterface => { // Handle both full names and short names return implementedInterface === fullName || implementedInterface === iface.name || `${type.namespace}.${implementedInterface}` === fullName; }); return implementsInterface && !type.name.startsWith('I') && typeFullName !== fullName; }) .map(type => `${type.namespace}.${type.name}`); return { baseInterface: fullName, derivedInterfaces, implementingTypes, depth: this.calculateInterfaceDepth(iface, types) }; } analyzeGenericTypeRelationship(genericDef, instances) { const fullName = `${genericDef.namespace}.${genericDef.name}`; const baseName = genericDef.name.replace('<T>', '').replace('<', '').replace('>', '').split('<')[0]; // Find instantiations of this generic definition const instantiations = instances .filter(instance => { // Check if this instance is based on the generic definition const instanceBaseName = instance.name.split('<')[0]; return instanceBaseName === baseName && instance.namespace === genericDef.namespace && instance.isGenericInstance; }) .map(instance => `${instance.namespace}.${instance.name}`); return { genericDefinition: fullName, namespace: genericDef.namespace, typeParameters: genericDef.genericParameters, constraints: genericDef.constraints, instantiations, usageFrequency: instantiations.length }; } analyzeConstraints(param, constraints) { let constraintType = 'class'; let violationRisk = 'low'; // Analyze constraint types for (const constraint of constraints) { if (constraint.includes('struct')) { constraintType = 'struct'; } else if (constraint.includes('new()')) { constraintType = 'new'; } else if (constraint.includes('class')) { constraintType = 'class'; } else if (constraint.includes('I') && constraint.length > 1) { constraintType = 'interface'; } } // Assess violation risk based on constraint complexity if (constraints.length > 2) { violationRisk = 'high'; } else if (constraints.length > 1) { violationRisk = 'medium'; } return { typeParameter: param, constraints, constraintType, violationRisk }; } analyzeInstantiationPattern(genericDef, instances) { const fullName = `${genericDef.namespace}.${genericDef.name}`; const baseName = genericDef.name.replace('<', '').replace('>', '').split('<')[0]; // Find all instantiations const allInstantiations = instances .filter(instance => instance.name.includes(baseName)) .map(instance => `${instance.namespace}.${instance.name}`); // Categorize instantiations const commonInstantiations = allInstantiations.filter(inst => inst.includes('String') || inst.includes('Int32') || inst.includes('Object')); const unusualInstantiations = allInstantiations.filter(inst => !commonInstantiations.includes(inst)); return { genericDefinition: fullName, commonInstantiations, unusualInstantiations, instantiationCount: allInstantiations.length }; } calculateGenericComplexityMetrics(definitions, instances) { const totalGenericTypes = definitions.length; const typeParameterCounts = definitions.map(def => def.genericParameters.length); const averageTypeParameters = typeParameterCounts.length > 0 ? typeParameterCounts.reduce((sum, count) => sum + count, 0) / typeParameterCounts.length : 0; const maxTypeParameters = Math.max(...typeParameterCounts, 0); const constraintComplexity = definitions.reduce((sum, def) => sum + def.constraints.length, 0); // Calculate nesting depth by analyzing generic parameter names const nestingDepth = Math.max(...definitions.map(def => Math.max(...def.genericParameters.map(param => (param.match(/</g) || []).length), 0)), 0); return { totalGenericTypes, averageTypeParameters, maxTypeParameters, constraintComplexity, nestingDepth }; } calculateInterfaceDepth(iface, types) { // Calculate the depth of interface inheritance let depth = 0; const visited = new Set(); const queue = [iface]; while (queue.length > 0) { const current = queue.shift(); const fullName = `${current.namespace}.${current.name}`; if (visited.has(fullName)) { continue; } visited.add(fullName); // Find interfaces that inherit from this one const derivedInterfaces = types.filter(type => type.interfaces.includes(fullName) && type.name.startsWith('I')); if (derivedInterfaces.length > 0) { depth++; queue.push(...derivedInterfaces); } } return depth; } createDependencyNode(type, allTypes) { const fullName = `${type.namespace}.${type.name}`; // Find dependencies (types this type depends on) const dependencies = []; if (type.baseType) { dependencies.push(type.baseType); } // Add full names for interfaces if they don't already have namespace const interfaceDependencies = type.interfaces.map(iface => { if (iface.includes('.')) { return iface; // Already has namespace } else { // Try to find the interface in the type list to get its full name const interfaceType = allTypes.find(t => t.name === iface); return interfaceType ? `${interfaceType.namespace}.${interfaceType.name}` : `Game.${iface}`; } }); dependencies.push(...interfaceDependencies); // Find dependents (types that depend on this type) const dependents = allTypes .filter(t => t.baseType === fullName || t.interfaces.includes(fullName)) .map(t => `${t.namespace}.${t.name}`); // Calculate centrality (simplified betweenness centrality) const centrality = (dependencies.length + dependents.length) / allTypes.length; return { typeName: `${type.namespace}.${type.name}`, // Use full name for easier lookup namespace: type.namespace, typeDefIndex: type.typeDefIndex, dependencies, dependents, dependencyCount: dependencies.length, dependentCount: dependents.length, centrality }; } createDependencyEdges(type, allTypes) { const edges = []; const fromType = `${type.namespace}.${type.name}`; // Create inheritance edges if (type.baseType) { edges.push({ from: fromType, to: type.baseType, dependencyType: 'inheritance', strength: 1.0 }); } // Create interface edges for (const interfaceName of type.interfaces) { edges.push({ from: fromType, to: interfaceName, dependencyType: 'interface', strength: 0.8 }); } // Create generic edges if (type.isGenericInstance || type.isGenericDefinition) { edges.push({ from: fromType, to: fromType, // Self-reference for generic complexity dependencyType: 'generic', strength: 0.6 }); } return edges; } calculateDependencyMetrics(nodes, edges) { const totalNodes = nodes.length; const totalEdges = edges.length; const dependencyCounts = nodes.map(node => node.dependencyCount); const averageDependencies = dependencyCounts.length > 0 ? dependencyCounts.reduce((sum, count) => sum + count, 0) / dependencyCounts.length : 0; const maxDependencies = Math.max(...dependencyCounts, 0); // Simplified cyclomatic complexity calculation const cyclomaticComplexity = totalEdges - totalNodes + 1; // Simplified modularity score const modularityScore = totalNodes > 0 ? 1 - (totalEdges / (totalNodes * totalNodes)) : 0; return { totalNodes, totalEdges, averageDependencies, maxDependencies, cyclomaticComplexity, modularityScore }; } createTypeClusters(nodes, edges) { const clusters = []; const visited = new Set(); // Simple clustering based on namespace const namespaceGroups = new Map(); for (const node of nodes) { if (!namespaceGroups.has(node.namespace)) { namespaceGroups.set(node.namespace, []); } namespaceGroups.get(node.namespace).push(node); } // Create clusters from namespace groups for (const [namespace, groupNodes] of namespaceGroups) { if (groupNodes.length > 1) { const types = groupNodes.map(node => `${node.namespace}.${node.typeName}`); // Calculate cohesion (internal connections) const internalEdges = edges.filter(edge => types.includes(edge.from) && types.includes(edge.to)); const cohesion = groupNodes.length > 1 ? internalEdges.length / (groupNodes.length * (groupNodes.length - 1)) : 0; // Calculate coupling (external connections) const externalEdges = edges.filter(edge => (types.includes(edge.from) && !types.includes(edge.to)) || (!types.includes(edge.from) && types.includes(edge.to))); const coupling = externalEdges.length / groupNodes.length; clusters.push({ clusterId: namespace, types, cohesion, coupling, size: groupNodes.length }); } } return clusters; } buildDependencyGraphForCycleDetection(types) { const graph = new Map(); for (const type of types) { const fullName = `${type.namespace}.${type.name}`; const dependencies = []; if (type.baseType) { dependencies.push(type.baseType); } dependencies.push(...type.interfaces); graph.set(fullName, dependencies); } return graph; } detectCyclesUsingDFS(graph) { const cycles = []; const visited = new Set(); const recursionStack = new Set(); const path = []; for (const [node] of graph) { if (!visited.has(node)) { this.dfsForCycles(node, graph, visited, recursionStack, path, cycles); } } return cycles; } dfsForCycles(node, graph, visited, recursionStack, path, cycles) { visited.add(node); recursionStack.add(node); path.push(node); const neighbors = graph.get(node) || []; for (const neighbor of neighbors) { if (!visited.has(neighbor)) { this.dfsForCycles(neighbor, graph, visited, recursionStack, path, cycles); } else if (recursionStack.has(neighbor)) { // Found a cycle const cycleStart = path.indexOf(neighbor); if (cycleStart !== -1) { const cycle = path.slice(cycleStart); cycle.push(neighbor); // Complete the cycle cycles.push([...cycle]); } } } recursionStack.delete(node); path.pop(); } analyzeCycle(cycle, types) { let cycleType = 'inheritance'; let severity = 'medium'; // Determine cycle type based on relationships const typeMap = new Map(); for (const type of types) { typeMap.set(`${type.namespace}.${type.name}`, type); } let hasInheritance = false; let hasInterface = false; let hasGeneric = false; for (let i = 0; i < cycle.length - 1; i++) { const fromType = typeMap.get(cycle[i]); const toTypeName = cycle[i + 1]; if (fromType) { if (fromType.baseType === toTypeName) { hasInheritance = true; } if (fromType.interfaces.includes(toTypeName)) { hasInterface = true; } if (fromType.isGenericDefinition || fromType.isGenericInstance) { hasGeneric = true; } } } // Determine primary cycle type if (hasInheritance) { cycleType = 'inheritance'; severity = 'critical'; // Inheritance cycles are most severe } else if (hasInterface) { cycleType = 'interface'; severity = 'high'; } else if (hasGeneric) { cycleType = 'generic'; severity = 'medium'; } else { cycleType = 'composition'; severity = 'low'; } // Adjust severity based on cycle length - shorter cycles are more severe if (cycle.length <= 2) { // Short cycles are more severe severity = severity === 'low' ? 'medium' : severity === 'medium' ? 'high' : 'critical'; } else if (cycle.length > 4) { // Longer cycles are less severe severity = severity === 'critical' ? 'high' : severity === 'high' ? 'medium' : 'low'; } return { cycle, cycleType, severity, impact: this.calculateCycleImpact(cycle, types) }; } calculateCycleImpact(cycle, types) { const impact = []; const typeMap = new Map(); for (const type of types) { typeMap.set(`${type.namespace}.${type.name}`, type); } // Find types that depend on any type in the cycle for (const type of types) { const fullName = `${type.namespace}.${type.name}`; if (!cycle.includes(fullName)) { const dependsOnCycle = type.baseType && cycle.includes(type.baseType) || type.interfaces.some(iface => cycle.includes(iface)); if (dependsOnCycle) { impact.push(fullName); } } } return impact; } generateResolutionSuggestion(circularRef) { let suggestionType = 'refactoring'; let description = ''; let effort = 'medium'; switch (circularRef.cycleType) { case 'inheritance': suggestionType = 'interface_extraction'; description = 'Extract common functionality into interfaces to break inheritance cycles'; effort = 'high'; break; case 'interface': suggestionType = 'dependency_injection'; description = 'Use dependency injection to decouple interface dependencies'; effort = 'medium'; break; case 'composition': suggestionType = 'refactoring'; description = 'Refactor composition relationships to eliminate circular dependencies'; effort = 'low'; break; case 'generic': suggestionType = 'refactoring'; description = 'Simplify generic type relationships to reduce circular constraints'; effort = 'medium'; break; } // Adjust effort based on cycle complexity if (circularRef.cycle.length > 4) { effort = effort === 'low' ? 'medium' : 'high'; } return { circularReference: circularRef.cycle, suggestionType, description, effort }; } analyzeTypeCompatibilityPair(fromType, toType, allTypes) { const fromFullName = `${fromType.namespace}.${fromType.name}`; const toFullName = `${toType.namespace}.${toType.name}`; // Identical types if (fromFullName === toFullName) { return { type: 'identical' }; } // Check inheritance relationship if (this.isAssignableViaInheritance(fromType, toType, allTypes)) { return { type: 'assignable', rule: { fromType: fromFullName, toType: toFullName, rule: 'inheritance_assignability', conditions: ['fromType inherits from toType'] } }; } // Check interface implementation if (this.isAssignableViaInterface(fromType, toType)) { return { type: 'assignable', rule: { fromType: fromFullName, toType: toFullName, rule: 'interface_assignability', conditions: ['fromType implements toType interface'] } }; } // Check generic compatibility if (this.isGenericCompatible(fromType, toType)) { return { type: 'convertible', conversionPath: { fromType: fromFullName, toType: toFullName, path: [fromFullName, toFullName], conversionType: 'explicit' } }; } // Check built-in conversions const conversionPath = this.findBuiltInConversionPath(fromType, toType); if (conversionPath) { return { type: 'convertible', conversionPath }; } // Types are incompatible return { type: 'incompatible', reason: this.determineIncompatibilityReason(fromType, toType) }; } isAssignableViaInheritance(fromType, toType, allTypes) { const toFullName = `${toType.namespace}.${toType.name}`; // Check direct inheritance if (fromType.baseType === toFullName) { return true; } // Check indirect inheritance if (fromType.baseType) { const baseType = allTypes.find(t => `${t.namespace}.${t.name}` === fromType.baseType); if (baseType) { return this.isAssignableViaInheritance(baseType, toType, allTypes); } } return false; } isAssignableViaInterface(fromType, toType) { const toFullName = `${toType.namespace}.${toType.name}`; return fromType.interfaces.includes(toFullName); } isGenericCompatible(fromType, toType) { // Basic generic compatibility check if (fromType.isGenericInstance && toType.isGenericDefinition) { const fromBaseName = fromType.name.split('<')[0]; const toBaseName = toType.name.split('<')[0]; return fromBaseName === toBaseName && fromType.namespace === toType.namespace; } return false; } findBuiltInConversionPath(fromType, toType) { // Basic built-in conversion paths const fromFullName = `${fromType.namespace}.${fromType.name}`; const toFullName = `${toType.namespace}.${toType.name}`; // Example: numeric conversions const numericTypes = ['System.Int32', 'System.Int64', 'System.Single', 'System.Double']; if (numericTypes.includes(fromFullName) && numericTypes.includes(toFullName)) { return { fromType: fromFullName, toType: toFullName, path: [fromFullName, toFullName], conversionType: 'implicit' }; } return null; } determineIncompatibilityReason(fromType, toType) { const fromFullName = `${fromType.namespace}.${fromType.name}`; const toFullName = `${toType.namespace}.${toType.name}`; if (fromType.namespace !== toType.namespace) { return `Different namespaces: ${fromType.namespace} vs ${toType.namespace}`; } if (fromType.isGenericDefinition !== toType.isGenericDefinition) { return 'Generic definition mismatch'; } return `No conversion path available from ${fromFullName} to ${toFullName}`; } addBuiltInCompatibilityRules(compatibilityMatrix, types) { // Add some basic built-in incompatibility rules for testing compatibilityMatrix.set('System.String->System.Int32', 'incompatible'); compatibilityMatrix.set('System.Int32->System.String', 'incompatible'); // Add generic type compatibility for (const type of types) { if (type.isGenericInstance && type.name.includes('List<System.String>')) { compatibilityMatrix.set(`${type.namespace}.${type.name}->System.Collections.Generic.IEnumerable<System.String>`, 'assignable'); compatibilityMatrix.set(`${type.namespace}.${type.name}->System.Collections.Generic.List<System.Int32>`, 'incompatible'); } } } } exports.TypeAnalyzer = TypeAnalyzer; //# sourceMappingURL=type-analyzer.js.map