TEST(plankton, env_resolution) { CREATE_RUNTIME(); test_resolver_data_t data; data.i0 = new_heap_instance(runtime, ROOT(runtime, empty_instance_species)); data.i1 = new_heap_instance(runtime, ROOT(runtime, empty_instance_species)); value_t i2 = new_heap_instance(runtime, ROOT(runtime, empty_instance_species)); value_mapping_t resolver; value_mapping_init(&resolver, value_to_int, &data); value_mapping_t access; value_mapping_init(&access, int_to_value, &data); value_t d0 = transcode_plankton(runtime, &resolver, &access, data.i0); ASSERT_TRUE(value_identity_compare(data.i0, d0)); value_t d1 = transcode_plankton(runtime, &resolver, &access, data.i1); ASSERT_TRUE(value_identity_compare(data.i1, d1)); value_t d2 = transcode_plankton(runtime, &resolver, &access, i2); ASSERT_FALSE(value_identity_compare(i2, d2)); value_t a0 = new_heap_array(runtime, 4); set_array_at(a0, 0, data.i0); set_array_at(a0, 1, data.i1); set_array_at(a0, 2, i2); set_array_at(a0, 3, data.i0); value_t da0 = transcode_plankton(runtime, &resolver, &access, a0); ASSERT_TRUE(value_identity_compare(data.i0, get_array_at(da0, 0))); ASSERT_TRUE(value_identity_compare(data.i1, get_array_at(da0, 1))); ASSERT_FALSE(value_identity_compare(i2, get_array_at(da0, 2))); ASSERT_TRUE(value_identity_compare(data.i0, get_array_at(da0, 3))); DISPOSE_RUNTIME(); }
value_t assembler_flush(assembler_t *assm) { // Copy the bytecode into a blob object. blob_t code_blob; short_buffer_flush(&assm->code, &code_blob); TRY_DEF(bytecode, new_heap_blob_with_data(assm->runtime, &code_blob)); // Invert the constant pool map into an array. value_t value_pool_map = assm->value_pool; size_t value_pool_size = get_id_hash_map_size(value_pool_map); TRY_DEF(value_pool, new_heap_array(assm->runtime, value_pool_size)); id_hash_map_iter_t iter; id_hash_map_iter_init(&iter, value_pool_map); size_t entries_seen = 0; while (id_hash_map_iter_advance(&iter)) { value_t key; value_t value; id_hash_map_iter_get_current(&iter, &key, &value); size_t index = get_integer_value(value); // Check that the entry hasn't been set already. CHECK_PHYLUM(tpNull, get_array_at(value_pool, index)); set_array_at(value_pool, index, key); entries_seen++; } CHECK_EQ("wrong number of entries", entries_seen, value_pool_size); return new_heap_code_block(assm->runtime, bytecode, value_pool, assm->high_water_mark); }
// Executes a method declaration on the given fragment. static value_t apply_method_declaration(value_t ambience, value_t decl, value_t fragment) { CHECK_FAMILY(ofMethodDeclarationAst, decl); CHECK_FAMILY(ofModuleFragment, fragment); runtime_t *runtime = get_ambience_runtime(ambience); // Look for the :builtin annotation on this method. value_t annots = get_method_declaration_ast_annotations(decl); value_t builtin_name = new_not_found_condition(); for (size_t i = 0; i < get_array_length(annots); i++) { value_t annot = get_array_at(annots, i); TRY_DEF(value, run_expression_until_condition(ambience, fragment, annot)); if (in_family(ofBuiltinMarker, value)) builtin_name = get_builtin_marker_name(value); } // Compile the method whether it's a builtin or not. This way we can reuse // the compilation code for both cases and just patch up the result after // the fact if it's a builtin. value_t method_ast = get_method_declaration_ast_method(decl); TRY_DEF(method, compile_method_ast_to_method(runtime, method_ast, fragment)); if (!in_condition_cause(ccNotFound, builtin_name)) { // This is a builtin so patch the method with the builtin implementation. TRY_DEF(impl, runtime_get_builtin_implementation(runtime, builtin_name)); value_t impl_code = get_builtin_implementation_code(impl); TRY(validate_builtin_method_binding(method, impl)); set_method_code(method, impl_code); value_t impl_flags = get_builtin_implementation_method_flags(impl); set_method_flags(method, impl_flags); } value_t methodspace = get_module_fragment_methodspace(fragment); TRY(add_methodspace_method(runtime, methodspace, method)); return success(); }
// Iteratively apply the elements of the unbound fragment to the partially // initialized bound fragment. static value_t apply_module_fragment_elements(binding_context_t *context, value_t unbound_fragment, value_t bound_fragment) { value_t elements = get_unbound_module_fragment_elements(unbound_fragment); for (size_t i = 0; i < get_array_length(elements); i++) { value_t element = get_array_at(elements, i); TRY(apply_unbound_fragment_element(context->ambience, element, bound_fragment)); } return success(); }
value_t module_loader_process_options(runtime_t *runtime, value_t self, value_t options) { CHECK_FAMILY(ofIdHashMap, options); value_t libraries = get_id_hash_map_at_with_default(options, RSTR(runtime, libraries), ROOT(runtime, empty_array)); for (size_t i = 0; i < get_array_length(libraries); i++) { value_t library_path = get_array_at(libraries, i); TRY(module_loader_read_library(runtime, self, library_path)); } return success(); }
TEST(plankton, references) { CREATE_RUNTIME(); value_t i0 = new_heap_instance(runtime, ROOT(runtime, empty_instance_species)); value_t i1 = new_heap_instance(runtime, ROOT(runtime, empty_instance_species)); value_t i2 = new_heap_instance(runtime, ROOT(runtime, empty_instance_species)); value_t array = new_heap_array(runtime, 6); set_array_at(array, 0, i0); set_array_at(array, 1, i2); set_array_at(array, 2, i0); set_array_at(array, 3, i1); set_array_at(array, 4, i2); set_array_at(array, 5, i1); value_t decoded = check_plankton(runtime, array); ASSERT_SAME(get_array_at(decoded, 0), get_array_at(decoded, 2)); ASSERT_NSAME(get_array_at(decoded, 0), get_array_at(decoded, 1)); ASSERT_SAME(get_array_at(decoded, 1), get_array_at(decoded, 4)); ASSERT_NSAME(get_array_at(decoded, 1), get_array_at(decoded, 3)); ASSERT_SAME(get_array_at(decoded, 3), get_array_at(decoded, 5)); DISPOSE_RUNTIME(); }
value_t build_call_tags_entries(runtime_t *runtime, value_t tags) { int64_t tag_count = get_array_length(tags); TRY_DEF(result, new_heap_pair_array(runtime, tag_count)); for (int64_t i = 0; i < tag_count; i++) { set_pair_array_first_at(result, i, get_array_at(tags, i)); // The offset is counted backwards because the argument evaluated last will // be at the top of the stack, that is, offset 0, and the first will be at // the bottom so has the highest offset. int64_t offset = tag_count - i - 1; set_pair_array_second_at(result, i, new_integer(offset)); } TRY(co_sort_pair_array(result)); IF_EXPENSIVE_CHECKS_ENABLED(check_call_tags_entries_unique(result)); return result; }
// Given an array of modules map builds a two-level map from paths to stages to // fragment entries. static value_t build_real_fragment_entries(binding_context_t *context, value_t modules) { runtime_t *runtime = get_ambience_runtime(context->ambience); for (size_t mi = 0; mi < get_array_buffer_length(modules); mi++) { value_t module = get_array_buffer_at(modules, mi); value_t path = get_unbound_module_path(module); value_t fragments = get_unbound_module_fragments(module); for (size_t fi = 0; fi < get_array_length(fragments); fi++) { value_t fragment = get_array_at(fragments, fi); value_t stage = get_unbound_module_fragment_stage(fragment); bool dummy = false; TRY_DEF(entry, binding_context_ensure_fragment_entry(context, stage, path, fragment, &dummy)); value_t imports = get_fragment_entry_imports(entry); value_t fragment_imports = get_unbound_module_fragment_imports(fragment); for (size_t ii = 0; ii < get_array_length(fragment_imports); ii++) { value_t import = get_array_at(fragment_imports, ii); TRY_DEF(ident, new_heap_identifier(runtime, present_stage(), import)); TRY(ensure_array_buffer_contains(runtime, imports, ident)); } } } return success(); }
// Ensures that the given unbound module is in the given array buffer, as well // as any other modules imported by the module. static value_t ensure_module_in_array(runtime_t *runtime, value_t array, value_t unbound_module) { CHECK_FAMILY(ofUnboundModule, unbound_module); if (in_array_buffer(array, unbound_module)) // If it's already there there's nothing to do. return success(); // Add the module. TRY(add_to_array_buffer(runtime, array, unbound_module)); // Scan through the imports and recursively add imported modules. Which // stage the module is imported into doesn't matter at this point, we just // have to enumerate them. value_t unbound_fragments = get_unbound_module_fragments(unbound_module); for (size_t fi = 0; fi < get_array_length(unbound_fragments); fi++) { value_t unbound_fragment = get_array_at(unbound_fragments, fi); value_t imports = get_unbound_module_fragment_imports(unbound_fragment); for (size_t ii = 0; ii < get_array_length(imports); ii++) { value_t import = get_array_at(imports, ii); TRY_DEF(imported_module, module_loader_lookup_module( deref(runtime->module_loader), import)); TRY(ensure_module_in_array(runtime, array, imported_module)); } } return success(); }
value_t get_call_data_value_at(value_t self, int64_t param_index) { value_t tags = get_call_data_tags(self); int64_t offset = get_call_tags_offset_at(tags, param_index); value_t values = get_call_data_values(self); return get_array_at(values, offset); }
// Returns the value at the given offset from the current pc. static value_t read_value(code_cache_t *cache, frame_t *frame, size_t offset) { size_t index = read_short(cache, frame, offset); return get_array_at(cache->value_pool, index); }