// Returns true iff the given identifier corresponds to a fragment that is // ready to be bound and hasn't already been bound. static bool should_fragment_be_bound(binding_context_t *context, value_t schedule, value_t ident) { // This fragment is already scheduled so we definitely don't want to bind it // again. if (is_fragment_scheduled(schedule, ident)) return false; // Grab the information we hold about the fragment. value_t path = get_identifier_path(ident); value_t stage = get_identifier_stage(ident); value_t module = get_id_hash_map_at(context->fragment_entry_map, path); value_t entry = get_id_hash_map_at(module, stage); value_t imports = get_fragment_entry_imports(entry); // Check whether all its explicit dependencies are satisfied. for (size_t i = 0; i < get_array_buffer_length(imports); i++) { value_t import = get_array_buffer_at(imports, i); if (!is_fragment_scheduled(schedule, import)) return false; } // Check if there is a preceding fragment and whether it has been bound. value_t entry_before = get_fragment_entry_before(module, stage); if (in_condition_cause(ccNotFound, entry_before)) { return true; } else { value_t before_ident = get_fragment_entry_identifier(entry_before); return is_fragment_scheduled(schedule, before_ident); } }
value_t module_loader_lookup_module(value_t self, value_t path) { value_t modules = get_module_loader_modules(self); value_t result = get_id_hash_map_at(modules, path); if (in_condition_cause(ccNotFound, result)) WARN("Module %v not found.", path); return result; }
// Creates and binds modules and fragments according to the given schedule. static value_t execute_binding_schedule(binding_context_t *context, value_t schedule) { runtime_t *runtime = get_ambience_runtime(context->ambience); for (size_t i = 0; i < get_array_buffer_length(schedule); i++) { value_t next = get_array_buffer_at(schedule, i); TOPIC_INFO(Library, "About to bind %v", next); value_t path = get_identifier_path(next); value_t stage = get_identifier_stage(next); // Create the bound module if it doesn't already exist. value_t bound_module = get_id_hash_map_at(context->bound_module_map, path); if (in_condition_cause(ccNotFound, bound_module)) { TRY_SET(bound_module, new_heap_empty_module(runtime, path)); TRY(set_id_hash_map_at(runtime, context->bound_module_map, path, bound_module)); } // Create the bound fragment. value_t bound_fragment = get_module_fragment_at(bound_module, stage); if (in_condition_cause(ccNotFound, bound_fragment)) { TRY_SET(bound_fragment, new_empty_module_fragment(runtime, stage, bound_module)); TRY(add_module_fragment(runtime, bound_module, bound_fragment)); } else { // An earlier phase needed a reference to this fragment so it has already // been created but not initialized yet. CHECK_EQ("Unexpected phase", get_module_fragment_epoch(bound_fragment), feUninitialized); TRY(init_empty_module_fragment(runtime, bound_fragment)); set_module_fragment_epoch(bound_fragment, feUnbound); } if (is_present_core(runtime, next)) { // TODO: this is a hack, there should be some other mechanism for // identifying the core module. This way of binding it also means it // gets bound at a time that's not particularly well-defined which is // also an issue. TOPIC_INFO(Library, "Binding present core to %v", bound_fragment); set_ambience_present_core_fragment(context->ambience, bound_fragment); } // Grab the unbound fragment we'll use to create the bound fragment. value_t module_entries = get_id_hash_map_at(context->fragment_entry_map, path); value_t fragment_entry = get_id_hash_map_at(module_entries, stage); // Bind the fragment based on the data from the entry. TRY(bind_module_fragment(context, fragment_entry, bound_fragment)); TOPIC_INFO(Library, "Done binding %v", next); } return success(); }
value_t get_type_parents(runtime_t *runtime, value_t space, value_t type) { value_t inheritance = get_methodspace_inheritance(space); value_t parents = get_id_hash_map_at(inheritance, type); if (in_condition_cause(ccNotFound, parents)) { return ROOT(runtime, empty_array_buffer); } else { return parents; } }
static value_t new_instance_of_string(runtime_t *runtime, value_t type) { value_t factories = ROOT(runtime, plankton_factories); value_t factory = get_id_hash_map_at(factories, type); if (in_family(ofFactory, factory)) { value_t new_instance_wrapper = get_factory_new_instance(factory); void *new_instance_ptr = get_void_p_value(new_instance_wrapper); factory_new_instance_t *new_instance = (factory_new_instance_t*) (intptr_t) new_instance_ptr; return new_instance(runtime); } else { return new_heap_seed(runtime, type, nothing()); } }
// Checks whether a fragment entry for the given stage and path already exists // and if not creates it. static value_t binding_context_ensure_fragment_entry(binding_context_t *context, value_t stage, value_t path, value_t fragment, bool *created) { CHECK_PHYLUM(tpStageOffset, stage); CHECK_FAMILY(ofPath, path); CHECK_FAMILY_OPT(ofUnboundModuleFragment, fragment); value_t path_map = context->fragment_entry_map; runtime_t *runtime = get_ambience_runtime(context->ambience); if (!has_id_hash_map_at(path_map, path)) { TRY_DEF(stage_map, new_heap_id_hash_map(runtime, 16)); TRY(set_id_hash_map_at(runtime, path_map, path, stage_map)); } value_t stage_map = get_id_hash_map_at(path_map, path); if (!has_id_hash_map_at(stage_map, stage)) { TRY_DEF(imports, new_heap_array_buffer(runtime, 4)); TRY_DEF(ident, new_heap_identifier(runtime, stage, path)); TRY_DEF(entry, new_heap_triple(runtime, fragment, imports, ident)); TRY(set_id_hash_map_at(runtime, stage_map, stage, entry)); *created = true; } return get_id_hash_map_at(stage_map, stage); }
static value_t set_heap_object_contents_with_utf8_type(runtime_t *runtime, value_t object, value_t header, value_t payload) { value_t factories = ROOT(runtime, plankton_factories); value_t factory = get_id_hash_map_at(factories, header); if (in_family(ofFactory, factory)) { value_t set_contents_wrapper = get_factory_set_contents(factory); void *set_contents_ptr = get_void_p_value(set_contents_wrapper); factory_set_contents_t *set_contents = (factory_set_contents_t*) (intptr_t) set_contents_ptr; return set_contents(object, runtime, payload); } else { set_seed_payload(object, payload); return success(); } }
// Performs a lookup for a single symbol scope. static value_t map_scope_lookup(map_scope_o *self, value_t symbol, binding_info_t *info_out) { value_t value = get_id_hash_map_at(self->map, symbol); if (in_condition_cause(ccNotFound, value)) { return scope_lookup(self->outer, symbol, info_out); } else { if (info_out != NULL) { binding_info_codec_t codec; codec.encoded = get_integer_value(value); *info_out = codec.decoded; } return success(); } }
value_t build_bound_module(value_t ambience, value_t unbound_module) { runtime_t *runtime = get_ambience_runtime(ambience); binding_context_t context; binding_context_init(&context, ambience); TRY_SET(context.bound_module_map, new_heap_id_hash_map(runtime, 16)); TRY_DEF(modules, build_transitive_module_array(runtime, unbound_module)); TRY(build_fragment_entry_map(&context, modules)); TRY_DEF(schedule, build_binding_schedule(&context)); TRY(execute_binding_schedule(&context, schedule)); value_t path = get_unbound_module_path(unbound_module); value_t result = get_id_hash_map_at(context.bound_module_map, path); CHECK_FALSE("module missing", in_condition_cause(ccNotFound, result)); return result; }
value_t get_or_create_methodspace_selector_slice(runtime_t *runtime, value_t self, value_t selector) { value_t cache_ptr = get_methodspace_cache_ptr(self); value_t cache = get_freeze_cheat_value(cache_ptr); // Create the cache if it doesn't exist. if (is_nothing(cache)) { TRY_SET(cache, new_heap_id_hash_map(runtime, 128)); set_freeze_cheat_value(cache_ptr, cache); } // Create the selector-specific cache if it doesn't exits. value_t slice = get_id_hash_map_at(cache, selector); if (in_condition_cause(ccNotFound, slice)) { TRY_SET(slice, create_methodspace_selector_slice(runtime, self, selector)); TRY(set_id_hash_map_at(runtime, cache, selector, slice)); } return slice; }
value_t add_methodspace_inheritance(runtime_t *runtime, value_t self, value_t subtype, value_t supertype) { CHECK_FAMILY(ofMethodspace, self); CHECK_MUTABLE(self); CHECK_FAMILY(ofType, subtype); CHECK_FAMILY(ofType, supertype); value_t inheritance = get_methodspace_inheritance(self); value_t parents = get_id_hash_map_at(inheritance, subtype); if (in_condition_cause(ccNotFound, parents)) { // Make the parents buffer small since most types don't have many direct // parents. If this fails nothing has happened. TRY_SET(parents, new_heap_array_buffer(runtime, 4)); // If this fails we've wasted some space allocating the parents array but // otherwise nothing has happened. TRY(set_id_hash_map_at(runtime, inheritance, subtype, parents)); } // If this fails we may have set the parents array of the subtype to an empty // array which is awkward but okay. invalidate_methodspace_caches(self); return add_to_array_buffer(runtime, parents, supertype); }
// Adds mappings in the namespace and imports in the methodspace for everything // imported by the given fragment. static value_t bind_module_fragment_imports(binding_context_t *context, value_t imports, value_t bound_fragment) { // Import the modules spaces into this fragment and create bindings in the // importspace. value_t methodspace = get_module_fragment_methodspace(bound_fragment); value_t importspace = get_module_fragment_imports(bound_fragment); runtime_t *runtime = get_ambience_runtime(context->ambience); for (size_t i = 0; i < get_array_buffer_length(imports); i++) { // Look up the imported module. value_t import_ident = get_array_buffer_at(imports, i); value_t import_path = get_identifier_path(import_ident); value_t import_head = get_path_head(import_path); value_t import_stage = get_identifier_stage(import_ident); value_t import_module = get_id_hash_map_at(context->bound_module_map, import_path); value_t import_fragment = get_module_fragment_at(import_module, import_stage); CHECK_TRUE("import not bound", is_module_fragment_bound(import_fragment)); value_t import_methods = get_module_fragment_methodspace(import_fragment); TRY(add_methodspace_import(runtime, methodspace, import_methods)); TRY(set_id_hash_map_at(runtime, importspace, import_head, import_fragment)); } return success(); }
// Add synthetic fragment entries corresponding to imported fragments where // there is no real fragment to import the fragment into. static value_t build_synthetic_fragment_entries(binding_context_t *context) { // Keep adding synthetic modules as long as changes are being made to the // map. We'll scan through the fragments currently in the map, then scan // through their imports, and for each check that the fragment that should // receive the import exists. If it doesn't it is created. loop: do { id_hash_map_iter_t module_iter; id_hash_map_iter_init(&module_iter, context->fragment_entry_map); while (id_hash_map_iter_advance(&module_iter)) { value_t module_path; value_t module_fragments; // Scan through the fragments. id_hash_map_iter_get_current(&module_iter, &module_path, &module_fragments); id_hash_map_iter_t fragment_iter; id_hash_map_iter_init(&fragment_iter, module_fragments); while (id_hash_map_iter_advance(&fragment_iter)) { value_t stage; value_t entry; id_hash_map_iter_get_current(&fragment_iter, &stage, &entry); value_t unbound_fragment = get_fragment_entry_fragment(entry); // If there is no fragment associated with this entry it is synthetic // and hence we're done. if (is_nothing(unbound_fragment)) continue; // Scan through the fragment's imports and ensure that their import // targets have been created. value_t imports = get_fragment_entry_imports(entry); for (size_t i = 0; i < get_array_buffer_length(imports); i++) { value_t import = get_array_buffer_at(imports, i); value_t import_fragment_stage = get_identifier_stage(import); if (!value_identity_compare(import_fragment_stage, present_stage())) // We'll record past imports but ignore them for the purposes of // closing the import map since they're redundant. continue; value_t import_module_path = get_identifier_path(import); value_t import_module = get_id_hash_map_at(context->fragment_entry_map, import_module_path); // Scan through the fragments of the imported module. id_hash_map_iter_t imported_fragment_iter; id_hash_map_iter_init(&imported_fragment_iter, import_module); bool has_changed_anything = false; while (id_hash_map_iter_advance(&imported_fragment_iter)) { value_t import_stage; value_t import_entry; id_hash_map_iter_get_current(&imported_fragment_iter, &import_stage, &import_entry); value_t target_stage = add_stage_offsets(import_stage, stage); // Ensure that there is a target entry to add the import to. If it // already exists this is a no-op, if it doesn't a synthetic entry // is created. TRY_DEF(target_entry, binding_context_ensure_fragment_entry( context, target_stage, module_path, nothing(), &has_changed_anything)); value_t target_imports = get_fragment_entry_imports(target_entry); value_t import_ident = get_fragment_entry_identifier(import_entry); if (!in_array_buffer(target_imports, import_ident)) { has_changed_anything = true; TRY(add_to_array_buffer(get_ambience_runtime(context->ambience), target_imports, import_ident)); } } // If any changes were made we have to start over. if (has_changed_anything) goto loop; } } } } while (false); return success(); }