From 08fff2d34209ec8715d753e6ee82c1e5e7b37408 Mon Sep 17 00:00:00 2001 From: Winford Date: Wed, 15 Jan 2025 22:49:34 +0000 Subject: [PATCH 1/2] Add missing port clause in memory.c Adds a missing clause to handle port terms in `memory_estimate_usage(term)` function that is necessary for formatting ports for io or other purposes. Signed-off-by: Winford --- src/libAtomVM/memory.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libAtomVM/memory.c b/src/libAtomVM/memory.c index 4bc84e0e7..183c22ac6 100644 --- a/src/libAtomVM/memory.c +++ b/src/libAtomVM/memory.c @@ -594,6 +594,10 @@ static void memory_scan_and_copy(HeapFragment *old_fragment, term *mem_start, co TRACE("Found PID (%" TERM_X_FMT ")\n", t); ptr++; + } else if (term_is_local_port(t)) { + TRACE("Found port (%" TERM_X_FMT ")\n", t); + ptr++; + } else if ((t & 0x3) == 0x0) { TRACE("Found boxed header (%" TERM_X_FMT ")\n", t); From b31dea19827d5557c83ad598b47806016f7df64e Mon Sep 17 00:00:00 2001 From: Winford Date: Wed, 15 Jan 2025 23:11:30 +0000 Subject: [PATCH 2/2] Add port support to io_lib.erl Add support for io_lib (and io, etc...) to format ports. Adds port_to_list/1 to erlang module exports to keep dialyzer happy. Add test/erlang_tests/test_port_to_list.erl to test result of port_to_list/1 using the "echo" port. Signed-off-by: Winford --- libs/estdlib/src/erlang.erl | 11 ++++ libs/estdlib/src/io_lib.erl | 2 + tests/erlang_tests/CMakeLists.txt | 2 + tests/erlang_tests/test_port_to_list.erl | 71 ++++++++++++++++++++++++ tests/test.c | 1 + 5 files changed, 87 insertions(+) create mode 100644 tests/erlang_tests/test_port_to_list.erl diff --git a/libs/estdlib/src/erlang.erl b/libs/estdlib/src/erlang.erl index 13cdbe8bb..dc3eaf124 100644 --- a/libs/estdlib/src/erlang.erl +++ b/libs/estdlib/src/erlang.erl @@ -74,6 +74,7 @@ integer_to_list/2, fun_to_list/1, pid_to_list/1, + port_to_list/1, ref_to_list/1, register/2, unregister/1, @@ -853,6 +854,16 @@ fun_to_list(_Fun) -> pid_to_list(_Pid) -> erlang:nif_error(undefined). +%%----------------------------------------------------------------------------- +%% @param Port port to convert to a string +%% @returns a string representation of the port +%% @doc Create a string representing a port. +%% @end +%%----------------------------------------------------------------------------- +-spec port_to_list(Port :: port()) -> string(). +port_to_list(_Port) -> + erlang:nif_error(undefined). + %%----------------------------------------------------------------------------- %% @param Ref reference to convert to a string %% @returns a string representation of the reference diff --git a/libs/estdlib/src/io_lib.erl b/libs/estdlib/src/io_lib.erl index 33255dc12..f5d0a3d6b 100644 --- a/libs/estdlib/src/io_lib.erl +++ b/libs/estdlib/src/io_lib.erl @@ -292,6 +292,8 @@ format_spw(_Format, T) when is_float(T) -> erlang:float_to_list(T); format_spw(_Format, T) when is_pid(T) -> erlang:pid_to_list(T); +format_spw(_Format, T) when is_port(T) -> + erlang:port_to_list(T); format_spw(_Format, T) when is_reference(T) -> erlang:ref_to_list(T); format_spw(_Format, T) when is_function(T) -> diff --git a/tests/erlang_tests/CMakeLists.txt b/tests/erlang_tests/CMakeLists.txt index 9178e32b6..651acfd6f 100644 --- a/tests/erlang_tests/CMakeLists.txt +++ b/tests/erlang_tests/CMakeLists.txt @@ -162,6 +162,7 @@ compile_erlang(test_concat_badarg) compile_erlang(register_and_whereis_badarg) compile_erlang(test_send) compile_erlang(test_open_port_badargs) +compile_erlang(test_port_to_list) compile_erlang(echo) compile_erlang(pingpong) compile_erlang(prime_ext) @@ -641,6 +642,7 @@ add_custom_target(erlang_test_modules DEPENDS register_and_whereis_badarg.beam test_send.beam test_open_port_badargs.beam + test_port_to_list.beam echo.beam pingpong.beam prime_ext.beam diff --git a/tests/erlang_tests/test_port_to_list.erl b/tests/erlang_tests/test_port_to_list.erl new file mode 100644 index 000000000..4d309e8f7 --- /dev/null +++ b/tests/erlang_tests/test_port_to_list.erl @@ -0,0 +1,71 @@ +%% +%% This file is part of AtomVM. +%% +%% Copyright (c) 2025 +%% All rights reserved. +%% +%% 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_port_to_list). + +-export([start/0, test_echo_port_to_list/0]). + +-define(PORTBEGIN, "#Port<0."). +-define(PORTEND, ">"). + +start() -> + test_echo_port_to_list(). + +test_echo_port_to_list() -> + Port = open_port({spawn, "echo"}, []), + validate_port_fmt(Port). + +%% internal +validate_port_fmt(Port) -> + case get_valid_portchars(Port) of + {ok, PortChars} -> + Res = port_to_list(Port) =:= flatten([?PORTBEGIN, PortChars, ?PORTEND], []), + case Res of + true -> 0; + _ -> 2 + end; + {invalid_format, Port} -> + 1 + end. + +get_valid_portchars(Port) -> + PList = port_to_list(Port), + Bin = list_to_binary(PList), + [_, Portend] = binary:split(Bin, <>), + [PortNum, _] = binary:split(Portend, <>), + try binary_to_integer(PortNum) of + Num when is_integer(Num) -> + {ok, binary_to_list(PortNum)}; + _ -> + {invalid_format, Port} + catch + _:_ -> + {invalid_format, Port} + end. + +flatten([], Accum) -> + Accum; +flatten([H | T], Accum) when is_list(H) -> + FlattenedT = flatten(T, Accum), + flatten(H, FlattenedT); +flatten([H | T], Accum) -> + FlattenedT = flatten(T, Accum), + [H | FlattenedT]. diff --git a/tests/test.c b/tests/test.c index 650df73a6..3f1d2d3d4 100644 --- a/tests/test.c +++ b/tests/test.c @@ -208,6 +208,7 @@ struct Test tests[] = { TEST_CASE_EXPECTED(register_and_whereis_badarg, 333), TEST_CASE(test_send), TEST_CASE_EXPECTED(test_open_port_badargs, -21), + TEST_CASE(test_port_to_list), TEST_CASE_EXPECTED(prime_ext, 1999), TEST_CASE_EXPECTED(test_try_case_end, 256), TEST_CASE(test_exception_classes),