Skip to content

Commit c4bc515

Browse files
committed
Implement code:get_object_code/1
Signed-off-by: Franciszek Kubis <[email protected]> # Conflicts: # src/libAtomVM/module.c # src/libAtomVM/module.h # src/libAtomVM/nifs.c # src/libAtomVM/nifs.gperf
1 parent f6d5d16 commit c4bc515

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
@@ -328,6 +328,8 @@ Module *module_new_from_iff_binary(GlobalContext *global, const void *iff_binary
328328
mod->fun_table = beam_file + offsets[FUNT];
329329
mod->str_table = beam_file + offsets[STRT];
330330
mod->str_table_len = sizes[STRT];
331+
mod->binary = beam_file;
332+
mod->binary_size = size;
331333
#ifndef AVM_NO_JIT
332334
if (offsets[AVMN]) {
333335
NativeCodeChunk *native_code = (NativeCodeChunk *) (beam_file + offsets[AVMN]);

src/libAtomVM/module.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ struct Module
124124
const uint8_t *line_refs_table;
125125
size_t locations_count;
126126
const uint8_t *locations_table;
127+
uint8_t *binary;
128+
size_t binary_size;
127129
#ifndef AVM_NO_JIT
128130
ModuleNativeEntryPoint native_code;
129131
#endif

src/libAtomVM/nifs.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ static term nif_code_all_loaded(Context *ctx, int argc, term argv[]);
196196
static term nif_code_load_abs(Context *ctx, int argc, term argv[]);
197197
static term nif_code_load_binary(Context *ctx, int argc, term argv[]);
198198
static term nif_code_ensure_loaded(Context *ctx, int argc, term argv[]);
199+
static term nif_code_get_object_code(Context *ctx, int argc, term argv[]);
199200
static term nif_code_server_is_loaded(Context *ctx, int argc, term argv[]);
200201
static term nif_code_server_resume(Context *ctx, int argc, term argv[]);
201202
#ifndef AVM_NO_JIT
@@ -747,6 +748,11 @@ static const struct Nif code_ensure_loaded_nif = {
747748
.nif_ptr = nif_code_ensure_loaded
748749
};
749750

751+
static const struct Nif code_get_object_code_nif = {
752+
.base.type = NIFFunctionType,
753+
.nif_ptr = nif_code_get_object_code
754+
};
755+
750756
static const struct Nif code_server_is_loaded_nif = {
751757
.base.type = NIFFunctionType,
752758
.nif_ptr = nif_code_server_is_loaded
@@ -5509,6 +5515,52 @@ static term nif_code_ensure_loaded(Context *ctx, int argc, term argv[])
55095515
return result;
55105516
}
55115517

5518+
static term nif_code_get_object_code(Context *ctx, int argc, term argv[])
5519+
{
5520+
UNUSED(argc);
5521+
term module_atom = argv[0];
5522+
VALIDATE_VALUE(module_atom, term_is_atom);
5523+
5524+
size_t module_name_len;
5525+
const uint8_t *module_name = atom_table_get_atom_string(ctx->global->atom_table, term_to_atom_index(module_atom), &module_name_len);
5526+
5527+
size_t filename_len = module_name_len + 6;
5528+
char *module_file_name = malloc(filename_len);
5529+
if (IS_NULL_PTR(module_file_name)) {
5530+
return ERROR_ATOM;
5531+
}
5532+
memcpy(module_file_name, module_name, module_name_len);
5533+
memcpy(module_file_name + module_name_len, ".beam", 6);
5534+
Module *module = globalcontext_load_module_from_avm(ctx->global, module_file_name);
5535+
5536+
if (IS_NULL_PTR(module)) {
5537+
module = sys_load_module_from_file(ctx->global, module_file_name);
5538+
}
5539+
if (UNLIKELY(!module)) {
5540+
free(module_file_name);
5541+
return ERROR_ATOM;
5542+
}
5543+
5544+
size_t result_size = TUPLE_SIZE(3) + term_binary_heap_size(module->binary_size) + LIST_SIZE(filename_len, 1);
5545+
if (UNLIKELY(memory_ensure_free_with_roots(ctx, result_size, 1, &module_atom, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
5546+
free(module_file_name);
5547+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
5548+
}
5549+
// Note: this assumes constness of module->binary and could be use-after-free if we allowed changing module bitcode at runtime.
5550+
// TODO: update this code when module unloading will be supported.
5551+
term binary = term_from_literal_binary((void *) module->binary, module->binary_size, &ctx->heap, ctx->global);
5552+
// TODO: this code has to be changed to return the complete path
5553+
term filename_term = term_from_string((const uint8_t *) module_file_name, filename_len, &ctx->heap);
5554+
term result = term_alloc_tuple(3, &ctx->heap);
5555+
5556+
term_put_tuple_element(result, 0, module_atom);
5557+
term_put_tuple_element(result, 1, binary);
5558+
term_put_tuple_element(result, 2, filename_term);
5559+
5560+
free(module_file_name);
5561+
return result;
5562+
}
5563+
55125564
static term nif_code_server_is_loaded(Context *ctx, int argc, term argv[])
55135565
{
55145566
UNUSED(argc);

src/libAtomVM/nifs.gperf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ code:load_binary/3, &code_load_binary_nif
177177
code:all_available/0, &code_all_available_nif
178178
code:all_loaded/0, &code_all_loaded_nif
179179
code:ensure_loaded/1, &code_ensure_loaded_nif
180+
code:get_object_code/1, &code_get_object_code_nif
180181
code_server:is_loaded/1, &code_server_is_loaded_nif
181182
code_server:resume/2, &code_server_resume_nif
182183
code_server:code_chunk/1, IF_HAVE_JIT(&code_server_code_chunk_nif)

tests/erlang_tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ compile_erlang(test_code_all_available_loaded)
542542
compile_erlang(test_code_load_binary)
543543
compile_erlang(test_code_load_abs)
544544
compile_erlang(test_code_ensure_loaded)
545+
compile_erlang(test_code_get_object_code)
545546
compile_erlang(test_add_avm_pack_binary)
546547
compile_erlang(test_add_avm_pack_file)
547548
compile_erlang(test_close_avm_pack)
@@ -1068,6 +1069,7 @@ set(erlang_test_beams
10681069

10691070
test_code_all_available_loaded.beam
10701071
test_code_load_binary.beam
1072+
test_code_get_object_code.beam
10711073
test_code_load_abs.beam
10721074
test_code_ensure_loaded.beam
10731075
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
@@ -544,6 +544,7 @@ struct Test tests[] = {
544544

545545
TEST_CASE(test_code_all_available_loaded),
546546
TEST_CASE_EXPECTED(test_code_load_binary, 24),
547+
TEST_CASE(test_code_get_object_code),
547548
TEST_CASE_EXPECTED(test_code_load_abs, 24),
548549
TEST_CASE(test_code_ensure_loaded),
549550
TEST_CASE_ATOMVM_ONLY(test_add_avm_pack_binary, 24),

0 commit comments

Comments
 (0)