Skip to content

Commit 0d96986

Browse files
committed
Implement code:get_object_code/1
# Conflicts: # src/libAtomVM/nifs.c
1 parent b21e92b commit 0d96986

File tree

8 files changed

+138
-1
lines changed

8 files changed

+138
-1
lines changed

libs/estdlib/src/code.erl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
load_binary/3,
3232
ensure_loaded/1,
3333
which/1,
34-
is_loaded/1
34+
is_loaded/1,
35+
get_object_code/1
3536
]).
3637

3738
%%-----------------------------------------------------------------------------
@@ -132,3 +133,14 @@ which(Module) ->
132133
{error, _} ->
133134
non_existing
134135
end.
136+
137+
%%-----------------------------------------------------------------------------
138+
%% @param Module module to get object code from
139+
%% @returns Tuple `{Module, Binary, Filename}' if successful, otherwise `error'.
140+
%% @doc Return module binary of a given module.
141+
%% @end
142+
%%-----------------------------------------------------------------------------
143+
-spec get_object_code(Module) -> {Module, Binary, Filename} | error when
144+
Module :: atom(), Binary :: binary(), Filename :: string().
145+
get_object_code(_Module) ->
146+
erlang:nif_error(undefined).

src/libAtomVM/module.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ Module *module_new_from_iff_binary(GlobalContext *global, const void *iff_binary
296296
mod->fun_table = beam_file + offsets[FUNT];
297297
mod->str_table = beam_file + offsets[STRT];
298298
mod->str_table_len = sizes[STRT];
299+
mod->binary = beam_file;
300+
mod->binary_size = size;
299301
uint32_t num_labels = ENDIAN_SWAP_32(mod->code->labels);
300302
mod->labels = calloc(num_labels, sizeof(void *));
301303
if (IS_NULL_PTR(mod->labels)) {

src/libAtomVM/module.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ struct Module
103103
const uint8_t *line_refs_table;
104104
size_t locations_count;
105105
const uint8_t *locations_table;
106+
uint8_t *binary;
107+
size_t binary_size;
106108

107109
unsigned int *line_refs_offsets;
108110
size_t line_refs_offsets_count;

src/libAtomVM/nifs.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ static term nif_code_all_loaded(Context *ctx, int argc, term argv[]);
194194
static term nif_code_load_abs(Context *ctx, int argc, term argv[]);
195195
static term nif_code_load_binary(Context *ctx, int argc, term argv[]);
196196
static term nif_code_ensure_loaded(Context *ctx, int argc, term argv[]);
197+
static term nif_code_get_object_code(Context *ctx, int argc, term argv[]);
197198
static term nif_erlang_module_loaded(Context *ctx, int argc, term argv[]);
198199
static term nif_erlang_nif_error(Context *ctx, int argc, term argv[]);
199200
static term nif_lists_reverse(Context *ctx, int argc, term argv[]);
@@ -730,6 +731,11 @@ static const struct Nif code_ensure_loaded_nif = {
730731
.nif_ptr = nif_code_ensure_loaded
731732
};
732733

734+
static const struct Nif code_get_object_code_nif = {
735+
.base.type = NIFFunctionType,
736+
.nif_ptr = nif_code_get_object_code
737+
};
738+
733739
static const struct Nif module_loaded_nif = {
734740
.base.type = NIFFunctionType,
735741
.nif_ptr = nif_erlang_module_loaded
@@ -5390,6 +5396,52 @@ static term nif_code_ensure_loaded(Context *ctx, int argc, term argv[])
53905396
return result;
53915397
}
53925398

5399+
static term nif_code_get_object_code(Context *ctx, int argc, term argv[])
5400+
{
5401+
UNUSED(argc);
5402+
term module_atom = argv[0];
5403+
VALIDATE_VALUE(module_atom, term_is_atom);
5404+
5405+
size_t module_name_len;
5406+
const uint8_t *module_name = atom_table_get_atom_string(ctx->global->atom_table, term_to_atom_index(module_atom), &module_name_len);
5407+
5408+
size_t filename_len = module_name_len + 6;
5409+
char *module_file_name = malloc(filename_len);
5410+
if (IS_NULL_PTR(module_file_name)) {
5411+
return ERROR_ATOM;
5412+
}
5413+
memcpy(module_file_name, module_name, module_name_len);
5414+
memcpy(module_file_name + module_name_len, ".beam", 6);
5415+
Module *module = globalcontext_load_module_from_avm(ctx->global, module_file_name);
5416+
5417+
if (IS_NULL_PTR(module)) {
5418+
module = sys_load_module_from_file(ctx->global, module_file_name);
5419+
}
5420+
if (UNLIKELY(!module)) {
5421+
free(module_file_name);
5422+
return ERROR_ATOM;
5423+
}
5424+
5425+
size_t result_size = TUPLE_SIZE(3) + term_binary_heap_size(module->binary_size) + LIST_SIZE(filename_len, 1);
5426+
if (UNLIKELY(memory_ensure_free_with_roots(ctx, result_size, 1, &module_atom, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
5427+
free(module_file_name);
5428+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
5429+
}
5430+
// Note: this assumes constness of module->binary and could be use-after-free if we allowed changing module bitcode at runtime.
5431+
// TODO: update this code when module unloading will be supported.
5432+
term binary = term_from_literal_binary((void *) module->binary, module->binary_size, &ctx->heap, ctx->global);
5433+
// TODO: this code has to be changed to return the complete path
5434+
term filename_term = term_from_string((const uint8_t *) module_file_name, filename_len, &ctx->heap);
5435+
term result = term_alloc_tuple(3, &ctx->heap);
5436+
5437+
term_put_tuple_element(result, 0, module_atom);
5438+
term_put_tuple_element(result, 1, binary);
5439+
term_put_tuple_element(result, 2, filename_term);
5440+
5441+
free(module_file_name);
5442+
return result;
5443+
}
5444+
53935445
static term nif_erlang_module_loaded(Context *ctx, int argc, term argv[])
53945446
{
53955447
UNUSED(argc);

src/libAtomVM/nifs.gperf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ code:load_binary/3, &code_load_binary_nif
175175
code:all_available/0, &code_all_available_nif
176176
code:all_loaded/0, &code_all_loaded_nif
177177
code:ensure_loaded/1, &code_ensure_loaded_nif
178+
code:get_object_code/1, &code_get_object_code_nif
178179
console:print/1, &console_print_nif
179180
base64:encode/1, &base64_encode_nif
180181
base64:decode/1, &base64_decode_nif

tests/erlang_tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ compile_erlang(test_code_all_available_loaded)
514514
compile_erlang(test_code_load_binary)
515515
compile_erlang(test_code_load_abs)
516516
compile_erlang(test_code_ensure_loaded)
517+
compile_erlang(test_code_get_object_code)
517518
compile_erlang(test_add_avm_pack_binary)
518519
compile_erlang(test_add_avm_pack_file)
519520
compile_erlang(test_close_avm_pack)
@@ -1035,6 +1036,7 @@ add_custom_target(erlang_test_modules DEPENDS
10351036

10361037
test_code_all_available_loaded.beam
10371038
test_code_load_binary.beam
1039+
test_code_get_object_code.beam
10381040
test_code_load_abs.beam
10391041
test_code_ensure_loaded.beam
10401042
test_add_avm_pack_binary.beam
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
%
2+
% This file is part of AtomVM.
3+
%
4+
% Copyright 2025 Franciszek Kubis <[email protected]>
5+
%
6+
% Licensed under the Apache License, Version 2.0 (the "License");
7+
% you may not use this file except in compliance with the License.
8+
% You may obtain a copy of the License at
9+
%
10+
% http://www.apache.org/licenses/LICENSE-2.0
11+
%
12+
% Unless required by applicable law or agreed to in writing, software
13+
% distributed under the License is distributed on an "AS IS" BASIS,
14+
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
% See the License for the specific language governing permissions and
16+
% limitations under the License.
17+
%
18+
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
%
20+
21+
-module(test_code_get_object_code).
22+
23+
-export([start/0, get_object_wrong_argument/1]).
24+
25+
-include("code_load/export_test_module_data.hrl").
26+
27+
start() ->
28+
ok = get_object_from_export_test_module(),
29+
ok = get_object_from_already_loaded_test_module(),
30+
ok = get_object_from_non_existing_module(),
31+
ok = ?MODULE:get_object_wrong_argument("a string"),
32+
ok = ?MODULE:get_object_wrong_argument(123),
33+
ok = ?MODULE:get_object_wrong_argument({1, "a"}),
34+
ok = ?MODULE:get_object_wrong_argument([1, b, 3]),
35+
0.
36+
37+
get_object_from_already_loaded_test_module() ->
38+
{test_code_get_object_code, Bin, _Filename} = code:get_object_code(?MODULE),
39+
{module, ?MODULE} = code:load_binary(
40+
?MODULE, atom_to_list(?MODULE) ++ ".beam", Bin
41+
),
42+
{module, ?MODULE} = code:ensure_loaded(?MODULE),
43+
ok.
44+
45+
get_object_from_export_test_module() ->
46+
Bin = ?EXPORT_TEST_MODULE_DATA,
47+
error = code:get_object_code(export_test_module),
48+
{module, export_test_module} = code:load_binary(
49+
export_test_module, "export_test_module.beam", Bin
50+
),
51+
{module, export_test_module} = code:ensure_loaded(export_test_module),
52+
error = code:get_object_code(export_test_module),
53+
24 = export_test_module:exported_func(4),
54+
ok.
55+
56+
get_object_from_non_existing_module() ->
57+
error = code:get_object_code(non_existing_module),
58+
ok.
59+
60+
get_object_wrong_argument(Argument) ->
61+
try code:get_object_code(Argument) of
62+
_ -> not_raised
63+
catch
64+
_:_ -> ok
65+
end.

tests/test.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ struct Test tests[] = {
543543

544544
TEST_CASE(test_code_all_available_loaded),
545545
TEST_CASE_EXPECTED(test_code_load_binary, 24),
546+
TEST_CASE(test_code_get_object_code),
546547
TEST_CASE_EXPECTED(test_code_load_abs, 24),
547548
TEST_CASE(test_code_ensure_loaded),
548549
TEST_CASE_ATOMVM_ONLY(test_add_avm_pack_binary, 24),

0 commit comments

Comments
 (0)