diff options
Diffstat (limited to 'src/program.c')
-rw-r--r-- | src/program.c | 135 |
1 files changed, 84 insertions, 51 deletions
diff --git a/src/program.c b/src/program.c index 082f9fd..8a17ac9 100644 --- a/src/program.c +++ b/src/program.c @@ -26,52 +26,26 @@ typedef struct { NumberUnion value; } Number; -/*doc(Bytecode) -The layout of the full bytecode is: Header (Intermediates Strings Functions External_Functions Instructions)* -*/ - -static CHECK_RESULT int amal_program_append_header(amal_program *self) { - /*doc(Bytecode header) - # Header layout - |Type|Field |Description | - |----|-------------|----------------------------------------------------------------------------| - |u32 |Magic number |The magic number used to identify an amalgam bytecode file. | - |u8 |Major version|The major version of the bytecode. Updates in this is a breaking change. | - |u8 |Minor version|The minor version of the bytecode. Updates in this are backwards compatible.| - |u8 |Patch version|The patch version of the bytecode. Updates in this are only minor bug fixes.| - - The versions in the header only changes for every release, not every change. - */ - - const u32 magic_number = AMAL_PROGRAM_MAGIC_NUMBER; - const u8 major_version = AMAL_PROGRAM_MAJOR_VERSION; - const u8 minor_version = AMAL_PROGRAM_MINOR_VERSION; - const u8 patch_version = AMAL_PROGRAM_PATCH_VERSION; - - return_if_error(buffer_append(&self->data, &magic_number, 4)); - return_if_error(buffer_append(&self->data, &major_version, 1)); - return_if_error(buffer_append(&self->data, &minor_version, 1)); - return_if_error(buffer_append(&self->data, &patch_version, 1)); - - return 0; -} - int amal_program_init(amal_program *self) { ignore_result_int(buffer_init(&self->data, NULL)); - self->string_indices = NULL; - self->extern_func_indices = NULL; - self->intermediates_start = NULL; - self->strings_start = NULL; - self->extern_funcs_start = NULL; - self->read_index = 0; - self->num_intermediates = 0; - self->num_strings = 0; - self->num_functions = 0; - self->num_extern_functions = 0; + self->string_indices = NULL; + self->extern_func_indices = NULL; + self->intermediates_start = NULL; + self->strings_start = NULL; + self->extern_funcs_start = NULL; + self->exported_funcs = NULL; + self->exported_funcs_end = NULL; + self->read_index = 0; + self->main_func_instruction_offset = ~0U; + self->num_intermediates = 0; + self->num_strings = 0; + self->num_functions = 0; + self->num_extern_functions = 0; + self->num_exported_functions = 0; cleanup_if_error(arena_allocator_init(&self->allocator)); cleanup_if_error(hash_map_init(&self->extern_funcs_map, &self->allocator, sizeof(ProgramExternFunc), hash_map_compare_string, amal_hash_string)); - cleanup_if_error(amal_program_append_header(self)); + cleanup_if_error(buffer_append_header(&self->data)); return 0; cleanup: @@ -131,6 +105,36 @@ static CHECK_RESULT int amal_program_get_extern_func_by_index(amal_program *self return 0; } +static CHECK_RESULT int amal_program_set_exported_function_instruction_offset_advance(amal_program *self, u32 instruction_offset) { + if(self->exported_funcs >= self->exported_funcs_end) { + amal_log_error("The number of exported functions in the instructions is more than the number of exported instructions in the header"); + return AMAL_PROGRAM_INSTRUCTION_INVALID_EXPORTED_FUNC_INDEX; + } + + { + u8 num_args; + u8 func_name_size; + + if((usize)(self->exported_funcs_end - self->exported_funcs) < sizeof(instruction_offset) + sizeof(num_args) + sizeof(func_name_size)) + return AMAL_PROGRAM_INSTRUCTION_INVALID_EXPORTED_FUNC_INDEX; + + am_memcpy(self->exported_funcs, &instruction_offset, sizeof(instruction_offset)); + num_args = self->exported_funcs[sizeof(instruction_offset)]; + func_name_size = self->exported_funcs[sizeof(instruction_offset) + sizeof(num_args)]; + self->exported_funcs += sizeof(instruction_offset) + sizeof(num_args) + sizeof(func_name_size); + if(self->main_func_instruction_offset == ~0U && am_memeql(self->exported_funcs, "main", 4)) + self->main_func_instruction_offset = instruction_offset; + + /* +1 to skip null-termination character */ + if((usize)(self->exported_funcs_end - self->exported_funcs) < func_name_size + 1U) + return AMAL_PROGRAM_INSTRUCTION_INVALID_EXPORTED_FUNC_INDEX; + + self->exported_funcs += func_name_size + 1; /* +1 to skip null-termination character */ + } + + return 0; +} + int amal_program_append_bytecode(amal_program *self, Bytecode *bytecode) { return buffer_append(&self->data, bytecode->data.data, bytecode->data.size); } @@ -158,14 +162,14 @@ static CHECK_RESULT int amal_program_read_header(amal_program *self) { am_memcpy(&patch_version, &self->data.data[self->read_index], sizeof(patch_version)); self->read_index += sizeof(u8); - if(magic_number != AMAL_PROGRAM_MAGIC_NUMBER) + if(magic_number != AMAL_BYTECODE_MAGIC_NUMBER) return AMAL_PROGRAM_INVALID_MAGIC_NUMBER; /* A program is only incompatible if the major version is newer than the version that is used to run it. TODO: Implement backwards compatible reads, starting from when the program bytecode breaks backwards compatibility */ - if(major_version > AMAL_PROGRAM_MAJOR_VERSION) + if(major_version > AMAL_BYTECODE_MAJOR_VERSION) return AMAL_PROGRAM_INCOMPATIBLE; return AMAL_PROGRAM_OK; @@ -237,7 +241,7 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) { am_memcpy(&string_size, &self->data.data[self->read_index], sizeof(string_size)); self->read_index += sizeof(string_size); - /* +1 to skip null-termination character */ + /* +1 to skip null-termination character */ if(bytes_left_to_read(self) < string_size + 1U) return AMAL_PROGRAM_INVALID_STRINGS; @@ -291,9 +295,9 @@ static CHECK_RESULT int amal_program_read_external_functions(amal_program *self) func_name_size = self->data.data[self->read_index + sizeof(num_args)]; self->read_index += sizeof(num_args) + sizeof(func_name_size); - /* +1 to skip null-termination character */ + /* +1 to skip null-termination character */ if(bytes_left_to_read(self) < func_name_size + 1U) - return AMAL_PROGRAM_INVALID_STRINGS; + return AMAL_PROGRAM_INVALID_EXTERNAL_FUNCTIONS; self->read_index += func_name_size + 1; /* +1 to skip null-termination character */ } @@ -303,6 +307,28 @@ static CHECK_RESULT int amal_program_read_external_functions(amal_program *self) return AMAL_PROGRAM_OK; } +static CHECK_RESULT int amal_program_read_exported_functions(amal_program *self) { + u32 export_funcs_size; + + if(!amal_program_read_advance(self, &self->num_exported_functions, sizeof(u16))) + return AMAL_PROGRAM_INVALID_EXPORTED_FUNCTIONS; + + if(!amal_program_read_advance(self, &export_funcs_size, sizeof(export_funcs_size))) + return AMAL_PROGRAM_INVALID_EXPORTED_FUNCTIONS; + + if(bytes_left_to_read(self) < export_funcs_size) + return AMAL_PROGRAM_INVALID_EXPORTED_FUNCTIONS_SIZE; + + /* + Exported functions doesn't need list of indices to the data, since exported functions + are always sorted in the instructions in the same order as list of exported functions + */ + self->exported_funcs = (u8*)(self->data.data + self->read_index); + self->exported_funcs_end = self->exported_funcs + export_funcs_size; + self->read_index += export_funcs_size; + return AMAL_PROGRAM_OK; +} + static CHECK_RESULT int amal_program_get_intermediate_by_index(amal_program *self, u16 index, Number *result) { if(index >= self->num_intermediates) return AMAL_PROGRAM_INSTRUCTION_INVALID_INTERMEDIATE_INDEX; @@ -507,7 +533,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ break; } case AMAL_OP_FUNC_START: { - u8 func_num_param_regs; + u8 func_flags; u16 func_num_local_var_regs; assert(!inside_func); @@ -515,9 +541,10 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ assert(func_counter < self->num_functions); ++func_counter; - func_num_param_regs = self->data.data[self->read_index]; - (void)func_num_param_regs; - am_memcpy(&func_num_local_var_regs, self->data.data + self->read_index + sizeof(func_num_param_regs), sizeof(func_num_local_var_regs)); + func_flags = self->data.data[self->read_index]; + am_memcpy(&func_num_local_var_regs, self->data.data + self->read_index + sizeof(func_flags), sizeof(func_num_local_var_regs)); + if(func_flags & FUNC_FLAG_EXPORTED) + return_if_error(amal_program_set_exported_function_instruction_offset_advance(self, amal_exec_get_code_offset(executor))); return_if_error(amal_exec_func_start(executor, func_num_local_var_regs)); self->read_index += 3; break; @@ -549,9 +576,15 @@ int amal_program_run(amal_program *self) { cleanup_if_error(amal_program_read_strings(self)); cleanup_if_error(amal_program_read_functions(self)); cleanup_if_error(amal_program_read_external_functions(self)); + cleanup_if_error(amal_program_read_exported_functions(self)); cleanup_if_error(amal_program_read_instructions(self, executor)); } - result = amal_executor_run(executor); + if(self->main_func_instruction_offset == ~0U) { + amal_log_error("The program is missing a main function"); + result = AMAL_PROGRAM_NO_MAIN_FUNC; + goto cleanup; + } + result = amal_executor_run(executor, self->main_func_instruction_offset); cleanup: amal_executor_deinit(executor); |