ModuleResolver – Resolución de Módulos y Paquetes
Archivo: src/module.rs — 507 líneas
¿Por qué existe?
Gestiona la carga, composición y recarga de módulos de código Forja, permitiendo que los
archivos .fa se importen entre sí (importar ruta) y que dependencias
externas provengan de paquetes publicados o módulos embebidos en el binario del compilador.
Es el sistema de resolución de dependencias del lenguaje.
Estructuras principales
ModuleInfo
Almacena la información completa de un módulo cargado.
| Campo | Tipo | Descripción |
source_path | String | Ruta absoluta al archivo fuente del módulo |
source_hash | u64 | Hash del contenido fuente para detectar cambios |
funciones | Vec<(String, SymId)> | Lista de funciones exportadas (nombre, SymId) |
variables_globales | Vec<(String, SymId)> | Lista de variables globales exportadas |
imports | Vec<ModuleId> | IDs de los módulos que este módulo importa |
dependents | Vec<ModuleId> | IDs de los módulos que dependen de este (para recarga en cascada) |
bytecode | Option<ModuleBytecode> | Bytecode compilado del módulo (opcional, se genera bajo demanda) |
version | u64 | Contador de versiones para detectar cambios |
ModuleCache
Cache de módulos con índices por ruta y por ID, más grafos de importación y dependencias.
| Campo | Tipo | Descripción |
por_ruta | HashMap<String, ModuleId> | Índice de módulos por ruta de archivo |
por_id | HashMap<ModuleId, ModuleInfo> | Índice de módulos por ID (SymId) |
grafo_importaciones | HashMap<ModuleId, Vec<ModuleId>> | Grafo dirigido de importaciones |
grafo_dependientes | HashMap<ModuleId, Vec<ModuleId>> | Grafo inverso de dependientes (para recarga en cascada) |
ModuleResolver
| Campo | Tipo | Descripción |
root_dir | PathBuf | Directorio raíz del proyecto (donde se resuelven rutas relativas) |
cache | HashMap<String, Programa> | Cache de ASTs resueltos por ruta |
package_resolver | Option<PackageResolver> | Resolvedor de paquetes externos (opcional) |
module_cache | ModuleCache | Cache estructurada de módulos con metadatos |
Métodos públicos
Constructor y configuración
| Firma | Descripción |
pub fn new(root_dir: &str) -> Self | Crea un nuevo ModuleResolver con el directorio raíz especificado y caches vacíos |
Resolución de módulos
| Firma | Descripción |
pub fn resolver(&mut self, ruta: &str) -> Result<Programa, Vec<ErrorForja>> |
Resuelve un módulo por ruta. Normaliza la ruta, verifica seguridad (path traversal),
lee el archivo, tokeniza, parsea, resuelve recursivamente imports y cachea el AST.
|
pub fn resolver_con_id(&mut self, ruta: &str) -> Result<(Programa, ModuleId), Vec<ErrorForja>> |
Similar a resolver() pero además retorna el ModuleId asignado al módulo.
Es el método principal de resolución (~188 líneas).
|
fn resolver_con_id_embebido(&mut self, ruta_embedded: &str) -> Result<(Programa, ModuleId), Vec<ErrorForja>> |
Resuelve módulos embebidos (prefijo embedded://). Busca el módulo en la
lista de stdlib embebida (stdlib_embedded.rs), lo parsea y lo registra
en el cache.
|
Recarga de módulos
| Firma | Descripción |
pub fn recargar(&mut self, module_id: ModuleId) -> Result<Programa, Vec<ErrorForja>> |
Recarga un módulo por su ID. Vuelve a leer el archivo fuente, re-parsea, re-compila y
actualiza el cache. Si el módulo ya no existe, lo elimina del cache y propaga la
invalidación a sus dependientes.
|
pub fn modulo_cambio(&mut self, ruta: &str) -> bool |
Verifica si un módulo ha cambiado comparando el hash actual del archivo con el
hash almacenado. Retorna cierto si el archivo fue modificado.
|
pub fn modulos_cambiados(&self) -> Vec<(ModuleId, String)> |
Retorna una lista de todos los módulos cuyo archivo fuente ha cambiado desde la
última recarga. Iteración sobre todos los módulos en cache comparando hashes.
|
Registro y consulta de módulos
| Firma | Descripción |
pub fn registrar_modulo(&mut self, module_id: ModuleId, info: ModuleInfo) |
Registra un módulo en el cache. Actualiza los índices por ruta y por ID,
y mantiene el grafo de importaciones/dependencias.
|
pub fn dependientes_de(&self, module_id: ModuleId) -> Option<&[ModuleId]> | Retorna los IDs de módulos que dependen del módulo especificado |
pub fn importaciones_de(&self, module_id: ModuleId) -> Option<&[ModuleId]> | Retorna los IDs de módulos que el módulo especificado importa |
Utilidades
| Firma | Descripción |
pub fn hash_fuente(ruta: &str) -> u64 |
Calcula un hash CRC32 del contenido de un archivo fuente. Usado para detectar
cambios en módulos. Si el archivo no existe, retorna 0.
|
Funciones auxiliares
dedup_declaraciones()
| Firma | Descripción |
pub fn dedup_declaraciones(decls: Vec<Declaracion>) -> Vec<Declaracion> |
Elimina declaraciones duplicadas basándose en una clave única que identifica
cada declaración: fn:nombre:tipos para funciones (incluye tipos de
parámetros para soportar sobrecarga), cls:nombre para clases,
var:nombre para variables globales. Si hay conflicto, la última
declaración prevalece.
|
formato_tipo_para_key()
| Firma | Descripción |
fn formato_tipo_para_key(t: &Tipo) -> String |
Convierte un Tipo a su representación textual para usar como parte
de la clave de deduplicación. Permite diferenciar sobrecargas de funciones
con tipos distintos en los parámetros.
|
Pipeline de resolución de módulos
┌─────────────┐
│ importar │
│ "ruta/foo" │
└──────┬──────┘
▼
┌──────────────────────────────┐
│ 1. Normalizar ruta │
│ - Unir con root_dir │
│ - Prevenir path traversal │
│ - Rechazar rutas absolutas│
└──────┬───────────────────────┘
▼
┌──────────────────────────────┐
│ 2. ¿Módulo embebido? │──▶ embedded:// → resolver_con_id_embebido
└──────┬───────────────────────┘
▼ (no)
┌──────────────────────────────┐
│ 3. ¿En cache? │──▶ Sí → retornar Programa cacheado
└──────┬───────────────────────┘
▼ (no)
┌──────────────────────────────┐
│ 4. Leer archivo fuente │
│ - Límite de tamaño │
│ - Hash CRC32 │
└──────┬───────────────────────┘
▼
┌──────────────────────────────┐
│ 5. Tokenizar (lexer) │
│ 6. Parsear (parser) → AST │
│ 7. Resolver imports recurs. │
└──────┬───────────────────────┘
▼
┌──────────────────────────────┐
│ 8. Deduplicar declaraciones │
│ 9. Cachear Programa │
│ 10. Registrar en ModuleCache │
│ 11. Retornar Programa │
└──────────────────────────────┘
Seguridad
- Path traversal: Rechaza rutas que contengan
.. (subir de directorio) - Rutas absolutas: Rechaza rutas que comiencen con
/ en sistemas Unix - Tamaño de archivo: Verifica límites de tamaño antes de leer
- Módulos embebidos: Solo se puede acceder a módulos
embedded:// que están en la lista blanca de stdlib
Relación con el resto del compilador
- Se invoca desde la CLI (
main.rs) y el REPL al cargar scripts - Utiliza
lexer.rs y parser.rs para transformar el código fuente en AST - Utiliza
stdlib_embedded.rs para resolver módulos embebidos de la stdlib - El
PackageResolver maneja dependencias externas (crates Forja publicados) - La VM (
vm_fast.rs) usa hot_swap_module() para recarga en caliente