Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion libs/estdlib/src/code.erl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
load_binary/3,
ensure_loaded/1,
which/1,
is_loaded/1
is_loaded/1,
get_object_code/1
]).

%%-----------------------------------------------------------------------------
Expand Down Expand Up @@ -132,3 +133,14 @@ which(Module) ->
{error, _} ->
non_existing
end.

%%-----------------------------------------------------------------------------
%% @param Module module to get object code from
%% @returns Tuple `{Module, Binary, Filename}' if successful, otherwise `error'.
%% @doc Return module binary of a given module.
%% @end
%%-----------------------------------------------------------------------------
-spec get_object_code(Module) -> {Module, Binary, Filename} | error when
Module :: atom(), Binary :: binary(), Filename :: string().
get_object_code(_Module) ->
erlang:nif_error(undefined).
2 changes: 2 additions & 0 deletions src/libAtomVM/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ Module *module_new_from_iff_binary(GlobalContext *global, const void *iff_binary
mod->fun_table = beam_file + offsets[FUNT];
mod->str_table = beam_file + offsets[STRT];
mod->str_table_len = sizes[STRT];
mod->binary = beam_file;
mod->binary_size = size;
#ifndef AVM_NO_JIT
if (offsets[AVMN]) {
NativeCodeChunk *native_code = (NativeCodeChunk *) (beam_file + offsets[AVMN]);
Expand Down
2 changes: 2 additions & 0 deletions src/libAtomVM/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ struct Module
const uint8_t *line_refs_table;
size_t locations_count;
const uint8_t *locations_table;
uint8_t *binary;
size_t binary_size;
#ifndef AVM_NO_JIT
ModuleNativeEntryPoint native_code;
#endif
Expand Down
48 changes: 48 additions & 0 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ static term nif_code_all_loaded(Context *ctx, int argc, term argv[]);
static term nif_code_load_abs(Context *ctx, int argc, term argv[]);
static term nif_code_load_binary(Context *ctx, int argc, term argv[]);
static term nif_code_ensure_loaded(Context *ctx, int argc, term argv[]);
static term nif_code_get_object_code(Context *ctx, int argc, term argv[]);
static term nif_code_server_is_loaded(Context *ctx, int argc, term argv[]);
static term nif_code_server_resume(Context *ctx, int argc, term argv[]);
#ifndef AVM_NO_JIT
Expand Down Expand Up @@ -747,6 +748,11 @@ static const struct Nif code_ensure_loaded_nif = {
.nif_ptr = nif_code_ensure_loaded
};

static const struct Nif code_get_object_code_nif = {
.base.type = NIFFunctionType,
.nif_ptr = nif_code_get_object_code
};

static const struct Nif code_server_is_loaded_nif = {
.base.type = NIFFunctionType,
.nif_ptr = nif_code_server_is_loaded
Expand Down Expand Up @@ -5509,6 +5515,48 @@ static term nif_code_ensure_loaded(Context *ctx, int argc, term argv[])
return result;
}

static term nif_code_get_object_code(Context *ctx, int argc, term argv[])
{
UNUSED(argc);
term module_atom = argv[0];
VALIDATE_VALUE(module_atom, term_is_atom);

size_t module_name_len;
const uint8_t *module_name = atom_table_get_atom_string(ctx->global->atom_table, term_to_atom_index(module_atom), &module_name_len);

size_t filename_len = module_name_len + 6;
char *module_file_name = malloc(filename_len);
if (IS_NULL_PTR(module_file_name)) {
return ERROR_ATOM;
}
memcpy(module_file_name, module_name, module_name_len);
memcpy(module_file_name + module_name_len, ".beam", 6);
Module *module = globalcontext_get_module(ctx->global, term_to_atom_index(module_atom));

if (UNLIKELY(!module)) {
free(module_file_name);
return ERROR_ATOM;
}
size_t result_size = TUPLE_SIZE(3) + term_binary_heap_size(module->binary_size) + LIST_SIZE(filename_len, 1);
if (UNLIKELY(memory_ensure_free_with_roots(ctx, result_size, 1, &module_atom, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
free(module_file_name);
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
// Note: this assumes constness of module->binary and could be use-after-free if we allowed changing module bitcode at runtime.
// TODO: update this code when module unloading will be supported.
term binary = term_from_literal_binary((void *) module->binary, module->binary_size, &ctx->heap, ctx->global);
// TODO: this code has to be changed to return the complete path
term filename_term = term_from_string((const uint8_t *) module_file_name, filename_len, &ctx->heap);
term result = term_alloc_tuple(3, &ctx->heap);

term_put_tuple_element(result, 0, module_atom);
term_put_tuple_element(result, 1, binary);
term_put_tuple_element(result, 2, filename_term);

free(module_file_name);
return result;
}

static term nif_code_server_is_loaded(Context *ctx, int argc, term argv[])
{
UNUSED(argc);
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/nifs.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ code:load_binary/3, &code_load_binary_nif
code:all_available/0, &code_all_available_nif
code:all_loaded/0, &code_all_loaded_nif
code:ensure_loaded/1, &code_ensure_loaded_nif
code:get_object_code/1, &code_get_object_code_nif
code_server:is_loaded/1, &code_server_is_loaded_nif
code_server:resume/2, &code_server_resume_nif
code_server:code_chunk/1, IF_HAVE_JIT(&code_server_code_chunk_nif)
Expand Down
2 changes: 2 additions & 0 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ compile_erlang(test_code_all_available_loaded)
compile_erlang(test_code_load_binary)
compile_erlang(test_code_load_abs)
compile_erlang(test_code_ensure_loaded)
compile_erlang(test_code_get_object_code)
compile_erlang(test_add_avm_pack_binary)
compile_erlang(test_add_avm_pack_file)
compile_erlang(test_close_avm_pack)
Expand Down Expand Up @@ -1068,6 +1069,7 @@ set(erlang_test_beams

test_code_all_available_loaded.beam
test_code_load_binary.beam
test_code_get_object_code.beam
test_code_load_abs.beam
test_code_ensure_loaded.beam
test_add_avm_pack_binary.beam
Expand Down
65 changes: 65 additions & 0 deletions tests/erlang_tests/test_code_get_object_code.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
%
% This file is part of AtomVM.
%
% Copyright 2025 Franciszek Kubis <franciszek.kubis@swmansion.com>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

-module(test_code_get_object_code).

-export([start/0, get_object_wrong_argument/1]).

-include("code_load/export_test_module_data.hrl").

start() ->
ok = get_object_from_export_test_module(),
ok = get_object_from_already_loaded_test_module(),
ok = get_object_from_non_existing_module(),
ok = ?MODULE:get_object_wrong_argument("a string"),
ok = ?MODULE:get_object_wrong_argument(123),
ok = ?MODULE:get_object_wrong_argument({1, "a"}),
ok = ?MODULE:get_object_wrong_argument([1, b, 3]),
0.

get_object_from_already_loaded_test_module() ->
{test_code_get_object_code, Bin, _Filename} = code:get_object_code(?MODULE),
{module, ?MODULE} = code:load_binary(
?MODULE, atom_to_list(?MODULE) ++ ".beam", Bin
),
{module, ?MODULE} = code:ensure_loaded(?MODULE),
ok.

get_object_from_export_test_module() ->
Bin = ?EXPORT_TEST_MODULE_DATA,
error = code:get_object_code(export_test_module),
{module, export_test_module} = code:load_binary(
export_test_module, "export_test_module.beam", Bin
),
{module, export_test_module} = code:ensure_loaded(export_test_module),
error = code:get_object_code(export_test_module),
24 = export_test_module:exported_func(4),
ok.

get_object_from_non_existing_module() ->
error = code:get_object_code(non_existing_module),
ok.

get_object_wrong_argument(Argument) ->
try code:get_object_code(Argument) of
_ -> not_raised
catch
_:_ -> ok
end.
1 change: 1 addition & 0 deletions tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ struct Test tests[] = {

TEST_CASE(test_code_all_available_loaded),
TEST_CASE_EXPECTED(test_code_load_binary, 24),
TEST_CASE(test_code_get_object_code),
TEST_CASE_EXPECTED(test_code_load_abs, 24),
TEST_CASE(test_code_ensure_loaded),
TEST_CASE_ATOMVM_ONLY(test_add_avm_pack_binary, 24),
Expand Down
Loading