Skip to content

Commit

Permalink
Forward port changes from v0.6 release branch
Browse files Browse the repository at this point in the history
Merge fixes (such as fconv issue) from release-0.6 branch into main.

Also it has been added a small change to allow running beams on CLI on
generic_unix.
  • Loading branch information
bettio committed Nov 24, 2024
2 parents 79bcfb9 + 729184d commit 6823697
Show file tree
Hide file tree
Showing 16 changed files with 169 additions and 35 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.6.6] - Unreleased

### Added

- Added the ability to run beams from the CLI for Generic Unix platform (it was already possible with nodejs and emscripten).

### Fixed

- Fixed specifications of nifs from `esp_adc` module
- ESP32: fix `gpio:init/1` on GPIO >= 32
- Adding missing check, passing a non numeric argument to a function expecting a floating point
might lead to a crash in certain situations.
- Fixed several bugs in `http_server` (#1366)
- Fixed generic\_unix `socket_driver` to return `{gen_tcp, closed}` when socket is closed on Linux instead of `{gen_tcp, {recv, 104}}`
- Fixed a memory leak where modules were not properly destroyed when the global context is destroyd

## [0.6.5] - 2024-10-15

Expand Down
2 changes: 1 addition & 1 deletion libs/eavmlib/src/ahttp_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ transform_options(Options) ->
undefined -> [{active, true} | Options];
_OtherValue -> Options
end,
[{binary, true} | WithActive].
[binary | WithActive].

take_parse_headers(Options) ->
case proplists:get_value(parse_headers, Options) of
Expand Down
16 changes: 11 additions & 5 deletions libs/eavmlib/src/http_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,16 @@ find_route(Method, Path, [{Target, Mod, _Opts} | T]) ->
end.

reply(StatusCode, ReplyBody, Conn) ->
{ok, Conn} = reply(
{ok, NewConn} = reply(
StatusCode, ReplyBody, [<<"Content-Type: text/html\r\nConnection: close\r\n">>], Conn
),
Socket = proplists:get_value(socket, Conn),
Socket = proplists:get_value(socket, NewConn),
gen_tcp:close(Socket),
ClosedConn = [{closed, true} | Conn],
ClosedConn =
case proplists:get_value(closed, NewConn) of
undefined -> [{closed, true} | NewConn];
true -> NewConn
end,
{ok, ClosedConn}.

reply(StatusCode, ReplyBody, ReplyHeaders, Conn) ->
Expand Down Expand Up @@ -107,13 +111,15 @@ reply(StatusCode, ReplyBody, ReplyHeaders, Conn) ->
{ok, ClosedConn}
end.

send_reply(Socket, [Chunk | Tail]) ->
send_reply(Socket, [Chunk | Tail]) when is_list(Chunk) orelse is_binary(Chunk) ->
case gen_tcp:send(Socket, Chunk) of
ok -> send_reply(Socket, Tail);
{error, _} = ErrorTuple -> ErrorTuple
end;
send_reply(_Socket, []) ->
ok.
ok;
send_reply(Socket, IOData) ->
gen_tcp:send(Socket, IOData).

code_to_status_string(200) ->
<<"200 OK">>;
Expand Down
2 changes: 2 additions & 0 deletions libs/exavmlib/lib/AVMPort.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
defmodule AVMPort do
# This avoids crashing the compiler at build time
@compile {:autoload, false}
# This avoids compiler warnings
@compile {:no_warn_undefined, [:port]}
@moduledoc """
This module provides an interface to communicate with AtomVM port processes.
The functionality of AVMPort.call is identical to the eavmlib :port.call/2 and
Expand Down
8 changes: 8 additions & 0 deletions src/libAtomVM/globalcontext.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ COLD_FUNC void globalcontext_destroy(GlobalContext *glb)
struct ListHead *item;
struct ListHead *tmp;

int module_index = glb->loaded_modules_count;
for (int i = 0; i < module_index; i++) {
module_destroy(glb->modules_by_index[i]);
}

struct ListHead *open_avm_packs = synclist_nolock(&glb->avmpack_data);
MUTABLE_LIST_FOR_EACH (item, tmp, open_avm_packs) {
struct AVMPackData *avmpack_data = GET_LIST_ENTRY(item, struct AVMPackData, avmpack_head);
Expand Down Expand Up @@ -656,6 +661,9 @@ Module *globalcontext_get_module(GlobalContext *global, AtomString module_name_a
if (UNLIKELY(!loaded_module || (globalcontext_insert_module(global, loaded_module) < 0))) {
fprintf(stderr, "Failed load module: %s\n", module_name);
free(module_name);
if (loaded_module) {
module_destroy(loaded_module);
}
return NULL;
}

Expand Down
2 changes: 1 addition & 1 deletion src/libAtomVM/globalcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ term globalcontext_existing_term_from_atom_string(GlobalContext *glb, AtomString
/**
* @brief Inserts a module to the modules table.
*
* @details Inserts an already loaded module to the modules table and assigns and index to it so it can be retrieved later by name or index.
* @details Inserts an already loaded module to the modules table and assigns and index to it so it can be retrieved later by name or index. The module is then owned by the global context that will destroy it when globalcontext_destroy is invoked.
* @param global the global context.
* @param module the module that will be added to the modules table.
* @returns the module index if successful, otherwise -1.
Expand Down
3 changes: 3 additions & 0 deletions src/libAtomVM/opcodesswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -5912,6 +5912,9 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
#ifdef IMPL_EXECUTE_LOOP
TRACE("fconv/2 %lx, fp%i\n", src_value, freg);
context_ensure_fpregs(ctx);
if (UNLIKELY(!term_is_number(src_value))) {
RAISE_ERROR(BADARITH_ATOM);
}
ctx->fr[freg] = term_conv_to_float(src_value);
#endif

Expand Down
1 change: 0 additions & 1 deletion src/platforms/emscripten/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ int main(int argc, char **argv)
global = NULL;

if (main_module) {
module_destroy(main_module);
main_module = NULL;
}

Expand Down
2 changes: 1 addition & 1 deletion src/platforms/generic_unix/lib/socket_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,7 @@ static EventListener *passive_recv_callback(GlobalContext *glb, EventListener *b
return NULL;
}
SocketDriverData *socket_data = (SocketDriverData *) ctx->platform_data;
if (len == 0) {
if (len == 0 || (len < 0 && errno == ECONNRESET)) {
// {Ref, {error, closed}}
BEGIN_WITH_STACK_HEAP(12, heap);
term pid = listener->pid;
Expand Down
53 changes: 28 additions & 25 deletions src/platforms/generic_unix/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void print_help(const char *program_name)
"\n"
"Syntax:\n"
"\n"
" %s [-h] [-v] <path-to-avm-file>+\n"
" %s [-h] [-v] <path-to-avm-or-beam-file>+\n"
"\n"
"Options:\n"
"\n"
Expand Down Expand Up @@ -88,9 +88,7 @@ int main(int argc, char **argv)

GlobalContext *glb = globalcontext_new();

const void *startup_beam = NULL;
uint32_t startup_beam_size;
const char *startup_module_name;
Module *startup_module = NULL;

for (int i = 1; i < argc; ++i) {
const char *ext = strrchr(argv[i], '.');
Expand All @@ -102,50 +100,56 @@ int main(int argc, char **argv)
}
synclist_append(&glb->avmpack_data, &avmpack_data->avmpack_head);

if (IS_NULL_PTR(startup_beam)) {
if (IS_NULL_PTR(startup_module)) {
const void *startup_beam = NULL;
const char *startup_module_name;
uint32_t startup_beam_size;
avmpack_find_section_by_flag(avmpack_data->data, 1, &startup_beam, &startup_beam_size, &startup_module_name);

if (startup_beam) {
avmpack_data->in_use = true;
startup_module = module_new_from_iff_binary(glb, startup_beam, startup_beam_size);
if (IS_NULL_PTR(startup_module)) {
fprintf(stderr, "Cannot load startup module: %s\n", startup_module_name);
return EXIT_FAILURE;
}
globalcontext_insert_module(glb, startup_module);
startup_module->module_platform_data = NULL;
}
}

} else if (i == 1 && ext && (strcmp(ext, ".beam") == 0)) {
} else if (ext && (strcmp(ext, ".beam") == 0)) {
MappedFile *mapped_file = mapped_file_open_beam(argv[i]);
if (!iff_is_valid_beam(mapped_file->mapped)) {
fprintf(stderr, "%s has invalid AVM Pack format.\n", argv[i]);
fprintf(stderr, "%s has invalid beam format.\n", argv[i]);
return EXIT_FAILURE;
}
startup_module_name = basename(argv[1]);
startup_beam = mapped_file->mapped;
startup_beam_size = mapped_file->size;

} else if (i == 1) {
fprintf(stderr, "%s is not an AVM or a BEAM file.\n", argv[i]);
return EXIT_FAILURE;
Module *mod = module_new_from_iff_binary(glb, mapped_file->mapped, mapped_file->size);
if (IS_NULL_PTR(mod)) {
fprintf(stderr, "Cannot load module: %s\n", argv[i]);
return EXIT_FAILURE;
}
globalcontext_insert_module(glb, mod);
mod->module_platform_data = NULL;
if (IS_NULL_PTR(startup_module) && module_search_exported_function(mod, ATOM_STR("\5", "start"), 0, glb) != 0) {
startup_module = mod;
}

} else {
fprintf(stderr, "%s is not an AVM file.\n", argv[i]);
fprintf(stderr, "%s is not an AVM or a BEAM file.\n", argv[i]);
return EXIT_FAILURE;
}
}

if (IS_NULL_PTR(startup_beam)) {
if (IS_NULL_PTR(startup_module)) {
fprintf(stderr, "Unable to locate entrypoint.\n");
return EXIT_FAILURE;
}

Module *mod = module_new_from_iff_binary(glb, startup_beam, startup_beam_size);
if (IS_NULL_PTR(mod)) {
fprintf(stderr, "Cannot load startup module: %s\n", startup_module_name);
return EXIT_FAILURE;
}
globalcontext_insert_module(glb, mod);
mod->module_platform_data = NULL;
Context *ctx = context_new(glb);
ctx->leader = 1;

context_execute_loop(ctx, mod, "start", 0);
context_execute_loop(ctx, startup_module, "start", 0);

term ret_value = ctx->x[0];
fprintf(stderr, "Return value: ");
Expand All @@ -161,7 +165,6 @@ int main(int argc, char **argv)

context_destroy(ctx);
globalcontext_destroy(glb);
module_destroy(mod);

return status;
}
2 changes: 2 additions & 0 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ compile_erlang(floatext)
compile_erlang(boxed_is_not_float)
compile_erlang(float_is_float)
compile_erlang(float_is_number)
compile_erlang(fconv_fail_invalid)

compile_erlang(float2list)
compile_erlang(float2bin)
Expand Down Expand Up @@ -860,6 +861,7 @@ add_custom_target(erlang_test_modules DEPENDS
boxed_is_not_float.beam
float_is_float.beam
float_is_number.beam
fconv_fail_invalid.beam

float2list.beam
float2bin.beam
Expand Down
40 changes: 40 additions & 0 deletions tests/erlang_tests/fconv_fail_invalid.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
%
% This file is part of AtomVM.
%
% Copyright 2024 Davide Bettio <[email protected]>
%
% 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(fconv_fail_invalid).

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

start() ->
try ?MODULE:deg_min_nsew_to_decimal({5, nil, e}) of
_Any -> -1
catch
error:badarith -> 0
end.

deg_min_nsew_to_decimal(Coord) ->
{Deg, Min, Nsew} = Coord,
DecimalCoord = Deg + Min / 60,
case Nsew of
n -> DecimalCoord;
s -> -DecimalCoord;
e -> DecimalCoord;
w -> -DecimalCoord
end.
1 change: 1 addition & 0 deletions tests/libs/eavmlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ set(ERLANG_MODULES
test_dir
test_file
test_ahttp_client
test_http_server
test_port
test_timer_manager
)
Expand Down
60 changes: 60 additions & 0 deletions tests/libs/eavmlib/test_http_server.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
%
% This file is part of AtomVM.
%
% Copyright 2024 Paul Guyot <[email protected]>
%
% 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_http_server).
-export([test/0, handle_req/3]).

test() ->
ok = test_chunk().

test_chunk() ->
Router = [
{"*", ?MODULE, []}
],
Pid = http_server:start_server(8080, Router),
{ok, Conn} = connect_client(5),
{ok, Conn2, _Ref} = ahttp_client:request(Conn, <<"GET">>, <<"/">>, [], undefined),
ok = loop_passive(Conn2, []),
exit(Pid, kill),
ok.

connect_client(Retries) when Retries > 0 ->
ConnectResult = ahttp_client:connect(http, "localhost", 8080, [{active, false}]),
case ConnectResult of
{ok, Conn} ->
{ok, Conn};
{error, _} = ConnectError ->
io:format("Request failed: ~p~n", [ConnectError]),
connect_client(Retries - 1)
end.

handle_req("GET", [], Conn) ->
Body = [34, <<"hello">>],
http_server:reply(200, Body, Conn).

% http_server doesn't send Content-Length, so ahttp_client doesn't know when it's done and reports closed connection
loop_passive(Conn, AccResp) ->
case ahttp_client:recv(Conn, 0) of
{ok, UpdatedConn, Responses} ->
loop_passive(UpdatedConn, lists:reverse(Responses, AccResp));
{error, {gen_tcp, closed}} ->
[{data, _DataRef, <<"\"hello">>}, {status, _StatusRef, 200}] = AccResp,
ok
end.
1 change: 1 addition & 0 deletions tests/libs/eavmlib/tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ start() ->
etest:test([
test_dir,
test_file,
test_http_server,
test_port,
test_timer_manager,
test_ahttp_client
Expand Down
2 changes: 1 addition & 1 deletion tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ struct Test tests[] = {
TEST_CASE_EXPECTED(boxed_is_not_float, 16),
TEST_CASE_EXPECTED(float_is_float, 32),
TEST_CASE_EXPECTED(float_is_number, 32),
TEST_CASE(fconv_fail_invalid),

TEST_CASE_EXPECTED(float2bin, 31),
TEST_CASE_EXPECTED(float2list, 31),
Expand Down Expand Up @@ -609,7 +610,6 @@ static int test_atom(struct Test *test)

context_destroy(ctx);
globalcontext_destroy(glb);
module_destroy(mod);
mapped_file_close(beam_file);
return result;
}
Expand Down

0 comments on commit 6823697

Please sign in to comment.