// SPDX-License-Identifier: MIT // Copyright (c) 2023 John Watts and the LuminaSensum contributors #define MODULE_INTERNAL_API #include "module.h" #include "error.h" #include "object.h" #include "string.h" #include "vm.h" // Useful macro for getting array size #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) // Use X macros to declare external module variables #define X(id) extern ModuleInfo module_info_##id; #include <module_ids.x> #undef X // Use X macros to build a list of modules #define X(id) &module_info_##id, static ModuleInfo *modules_list[] = { #include <module_ids.x> }; #undef X static const ModuleInfo *info_by_name(const char *name) { for (unsigned int i = 0; i < ARRAY_SIZE(modules_list); ++i) { ModuleInfo *info = modules_list[i]; if (strcmp(info->name, name) == 0) return info; } return NULL; } struct module_stack { ModuleInfo *elems[64]; int next; }; // Checks if an info is in a stack static int in_stack(const ModuleInfo *info, struct module_stack *stack) { for (int i = 0; i < stack->next; ++i) { ModuleInfo *elem = stack->elems[i]; if (elem == info) return 1; } return 0; } // Traverses a module use dependency tree, checking for circular dependencies // This uses a depth-first search with a stack for traversing // The resulting visited list is the reverse load order static void traverse_module_uses( const ModuleInfo *start, struct module_stack *visited) { static struct module_stack traversing = {0}; if (sizeof(modules_list) > sizeof(traversing.elems)) abort_print("Modules too big to traverse"); if (in_stack(start, visited)) return; traversing.next = 0; traversing.elems[traversing.next++] = start; do { ModuleInfo *cur = traversing.elems[traversing.next - 1]; const char **use = cur->uses; while (*use) { const ModuleInfo *info = info_by_name(*use); if (info == NULL) abort_print("Couldn't find module"); if (in_stack(info, &traversing)) abort_print("Cyclic dependency found"); if (!in_stack(info, visited)) { traversing.elems[traversing.next++] = info; break; } use++; } if (*use == NULL) { traversing.elems[traversing.next--] = NULL; visited->elems[visited->next++] = cur; } } while (traversing.next != -1); } Object module_find(VmState state, const char *name) { const ModuleInfo *info = info_by_name(name); vm_abort_if(state, !info, "Unable to find module!"); static struct module_stack visited = {0}; if (sizeof(modules_list) > sizeof(visited.elems)) abort_print("Modules too big to track"); traverse_module_uses(info, &visited); // TODO: Does nothing traverse_module_uses(info, &visited); // TODO: Does nothing return info->create(state); }