diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index 561e2f8ec1..7cde2bb495 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -440,10 +440,11 @@ static struct ResourceMonitor *context_monitors_handle_terminate(Context *ctx) term_put_tuple_element(info_tuple, 1, ref); if (ctx->native_handler != NULL) { term_put_tuple_element(info_tuple, 2, PORT_ATOM); + term_put_tuple_element(info_tuple, 3, term_port_from_local_process_id(ctx->process_id)); } else { term_put_tuple_element(info_tuple, 2, PROCESS_ATOM); + term_put_tuple_element(info_tuple, 3, term_from_local_process_id(ctx->process_id)); } - term_put_tuple_element(info_tuple, 3, term_from_local_process_id(ctx->process_id)); term_put_tuple_element(info_tuple, 4, ctx->exit_reason); mailbox_send(target, info_tuple); diff --git a/src/libAtomVM/externalterm.c b/src/libAtomVM/externalterm.c index f3dca2a2f5..bc6f048712 100644 --- a/src/libAtomVM/externalterm.c +++ b/src/libAtomVM/externalterm.c @@ -51,6 +51,7 @@ #define EXPORT_EXT 113 #define MAP_EXT 116 #define SMALL_ATOM_UTF8_EXT 119 +#define V4_PORT_EXT 120 #define INVALID_TERM_SIZE -1 #define NEW_FLOAT_EXT_SIZE 9 @@ -422,6 +423,31 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb) WRITE_32_UNALIGNED(buf + k + 8, term_to_external_node_creation(t)); } return k + 12; + } else if (term_is_local_port(t)) { + if (!IS_NULL_PTR(buf)) { + buf[0] = V4_PORT_EXT; + } + size_t k = 1; + term node_name = glb->node_name; + uint32_t creation = node_name == NONODE_AT_NOHOST_ATOM ? 0 : glb->creation; + k += serialize_term(IS_NULL_PTR(buf) ? NULL : buf + k, node_name, glb); + if (!IS_NULL_PTR(buf)) { + WRITE_64_UNALIGNED(buf + k, term_to_local_process_id(t)); + WRITE_32_UNALIGNED(buf + k + 8, creation); // creation + } + return k + 12; + } else if (term_is_external_port(t)) { + if (!IS_NULL_PTR(buf)) { + buf[0] = V4_PORT_EXT; + } + size_t k = 1; + term node = term_to_external_node(t); + k += serialize_term(IS_NULL_PTR(buf) ? NULL : buf + k, node, glb); + if (!IS_NULL_PTR(buf)) { + WRITE_64_UNALIGNED(buf + k, term_to_external_port_number(t)); + WRITE_32_UNALIGNED(buf + k + 8, term_to_external_node_creation(t)); + } + return k + 12; } else if (term_is_local_reference(t)) { if (!IS_NULL_PTR(buf)) { buf[0] = NEWER_REFERENCE_EXT; @@ -759,6 +785,34 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm } } + case V4_PORT_EXT: { + size_t node_size; + term node = parse_external_terms(external_term_buf + 1, &node_size, copy, heap, glb); + if (UNLIKELY(!term_is_atom(node))) { + return term_invalid_term(); + } + uint64_t number = READ_64_UNALIGNED(external_term_buf + node_size + 1); + uint32_t creation = READ_32_UNALIGNED(external_term_buf + node_size + 9); + *eterm_size = node_size + 13; + if (node != NONODE_AT_NOHOST_ATOM) { + term this_node = glb->node_name; + uint32_t this_creation = this_node == NONODE_AT_NOHOST_ATOM ? 0 : glb->creation; + if (node == this_node && creation == this_creation) { + if (UNLIKELY(number > TERM_MAX_LOCAL_PROCESS_ID)) { + return term_invalid_term(); + } + return term_port_from_local_process_id(number); + } else { + return term_from_external_port_number(node, number, creation, heap); + } + } else { + if (UNLIKELY(number > TERM_MAX_LOCAL_PROCESS_ID || creation != 0)) { + return term_invalid_term(); + } + return term_port_from_local_process_id(number); + } + } + case NEWER_REFERENCE_EXT: { uint16_t len = READ_16_UNALIGNED(external_term_buf + 1); if (UNLIKELY(len > 5)) { @@ -1079,7 +1133,8 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini return 0; } - case NEW_PID_EXT: { + case NEW_PID_EXT: + case V4_PORT_EXT: { if (UNLIKELY(remaining < 1)) { return INVALID_TERM_SIZE; } diff --git a/src/libAtomVM/memory.c b/src/libAtomVM/memory.c index 51870e04d0..c45e3393a5 100644 --- a/src/libAtomVM/memory.c +++ b/src/libAtomVM/memory.c @@ -479,8 +479,7 @@ unsigned long memory_estimate_usage(term t) } else if (term_is_local_pid(t)) { t = temp_stack_pop(&temp_stack); - } else if (term_is_external_pid(t)) { - acc += EXTERNAL_PID_SIZE; + } else if (term_is_local_port(t)) { t = temp_stack_pop(&temp_stack); } else if (term_is_nonempty_list(t)) { @@ -628,6 +627,10 @@ static void memory_scan_and_copy(HeapFragment *old_fragment, term *mem_start, co TRACE("- Found external pid.\n"); break; + case TERM_BOXED_EXTERNAL_PORT: + TRACE("- Found external port.\n"); + break; + case TERM_BOXED_EXTERNAL_REF: TRACE("- Found external ref.\n"); break; @@ -756,6 +759,10 @@ static void memory_scan_and_rewrite(size_t count, term *terms, const term *old_s ptr += EXTERNAL_PID_SIZE - 1; break; + case TERM_BOXED_EXTERNAL_PORT: + ptr += EXTERNAL_PORT_SIZE - 1; + break; + case TERM_BOXED_EXTERNAL_REF: ptr += EXTERNAL_REF_SIZE - 1; break; @@ -833,6 +840,9 @@ HOT_FUNC static term memory_shallow_copy_term(HeapFragment *old_fragment, term t } else if (term_is_local_pid(t)) { return t; + } else if (term_is_local_port(t)) { + return t; + } else if (term_is_cp(t)) { // CP is valid only on stack return t; diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 883291fb18..51cbdf872b 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -159,6 +159,7 @@ static term nif_ets_lookup(Context *ctx, int argc, term argv[]); static term nif_ets_lookup_element(Context *ctx, int argc, term argv[]); static term nif_ets_delete(Context *ctx, int argc, term argv[]); static term nif_erlang_pid_to_list(Context *ctx, int argc, term argv[]); +static term nif_erlang_port_to_list(Context *ctx, int argc, term argv[]); static term nif_erlang_ref_to_list(Context *ctx, int argc, term argv[]); static term nif_erlang_fun_to_list(Context *ctx, int argc, term argv[]); static term nif_erlang_function_exported(Context *ctx, int argc, term argv[]); @@ -583,6 +584,12 @@ static const struct Nif pid_to_list_nif = .nif_ptr = nif_erlang_pid_to_list }; +static const struct Nif port_to_list_nif = +{ + .base.type = NIFFunctionType, + .nif_ptr = nif_erlang_port_to_list +}; + static const struct Nif ref_to_list_nif = { .base.type = NIFFunctionType, @@ -985,7 +992,7 @@ static term nif_erlang_open_port_2(Context *ctx, int argc, term argv[]) if (!new_ctx) { RAISE_ERROR(BADARG_ATOM); } else { - return term_from_local_process_id(new_ctx->process_id); + return term_port_from_local_process_id(new_ctx->process_id); } } @@ -996,7 +1003,7 @@ static term nif_erlang_register_2(Context *ctx, int argc, term argv[]) term reg_name_term = argv[0]; VALIDATE_VALUE(reg_name_term, term_is_atom); term pid_or_port_term = argv[1]; - VALIDATE_VALUE(pid_or_port_term, term_is_local_pid); + VALIDATE_VALUE(pid_or_port_term, term_is_local_pid_or_port); int atom_index = term_to_atom_index(reg_name_term); int32_t pid = term_to_local_process_id(pid_or_port_term); @@ -1062,7 +1069,7 @@ static NativeHandlerResult process_echo_mailbox(Context *ctx) } result = NativeTerminate; term reply = term_alloc_tuple(2, &ctx->heap); - term_put_tuple_element(reply, 0, term_from_local_process_id(ctx->process_id)); + term_put_tuple_element(reply, 0, term_port_from_local_process_id(ctx->process_id)); term_put_tuple_element(reply, 1, CLOSED_ATOM); port_send_message(ctx->global, pid, reply); } else { @@ -1094,7 +1101,7 @@ static NativeHandlerResult process_console_message(Context *ctx, term msg) result = NativeTerminate; term pid = term_get_tuple_element(msg, 0); term reply = term_alloc_tuple(2, &ctx->heap); - term_put_tuple_element(reply, 0, term_from_local_process_id(ctx->process_id)); + term_put_tuple_element(reply, 0, term_port_from_local_process_id(ctx->process_id)); term_put_tuple_element(reply, 1, CLOSED_ATOM); port_send_message(ctx->global, pid, reply); } else if (is_tagged_tuple(msg, IO_REQUEST_ATOM, 4)) { @@ -1400,7 +1407,7 @@ static term nif_erlang_send_2(Context *ctx, int argc, term argv[]) term target = argv[0]; GlobalContext *glb = ctx->global; - if (term_is_local_pid(target)) { + if (term_is_local_pid_or_port(target)) { int32_t local_process_id = term_to_local_process_id(target); globalcontext_send_message(glb, local_process_id, argv[1]); @@ -3454,6 +3461,23 @@ static term nif_erlang_pid_to_list(Context *ctx, int argc, term argv[]) return make_list_from_ascii_buf((uint8_t *) buf, str_len, ctx); } +static term nif_erlang_port_to_list(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + term t = argv[0]; + VALIDATE_VALUE(t, term_is_port); + size_t max_len = term_is_external(t) ? EXTERNAL_PORT_AS_CSTRING_LEN : LOCAL_PORT_AS_CSTRING_LEN; + + char buf[max_len]; + int str_len = term_snprint(buf, max_len, t, ctx->global); + if (UNLIKELY(str_len < 0)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + return make_list_from_ascii_buf((uint8_t *) buf, str_len, ctx); +} + static term nif_erlang_ref_to_list(Context *ctx, int argc, term argv[]) { UNUSED(argc); @@ -3715,7 +3739,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) RAISE_ERROR(BADARG_ATOM); } - VALIDATE_VALUE(target_pid, term_is_local_pid); + VALIDATE_VALUE(target_pid, term_is_local_pid_or_port); int local_process_id = term_to_local_process_id(target_pid); Context *target = globalcontext_get_process_lock(ctx->global, local_process_id); @@ -3783,7 +3807,7 @@ static term nif_erlang_link(Context *ctx, int argc, term argv[]) term target_pid = argv[0]; - VALIDATE_VALUE(target_pid, term_is_local_pid); + VALIDATE_VALUE(target_pid, term_is_local_pid_or_port); int local_process_id = term_to_local_process_id(target_pid); Context *target = globalcontext_get_process_lock(ctx->global, local_process_id); @@ -3814,7 +3838,7 @@ static term nif_erlang_unlink(Context *ctx, int argc, term argv[]) term target_pid = argv[0]; - VALIDATE_VALUE(target_pid, term_is_local_pid); + VALIDATE_VALUE(target_pid, term_is_local_pid_or_port); int local_process_id = term_to_local_process_id(target_pid); Context *target = globalcontext_get_process_lock(ctx->global, local_process_id); diff --git a/src/libAtomVM/nifs.gperf b/src/libAtomVM/nifs.gperf index 14968532c1..77f0e8c2af 100644 --- a/src/libAtomVM/nifs.gperf +++ b/src/libAtomVM/nifs.gperf @@ -116,6 +116,7 @@ erlang:throw/1, &throw_nif erlang:raise/3, &raise_nif erlang:unlink/1, &unlink_nif erlang:pid_to_list/1, &pid_to_list_nif +erlang:port_to_list/1, &port_to_list_nif erlang:ref_to_list/1, &ref_to_list_nif erlang:fun_to_list/1, &fun_to_list_nif erlang:function_exported/3, &function_exported_nif diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index 240ad56263..da468e8451 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -2384,7 +2384,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #ifdef IMPL_EXECUTE_LOOP term recipient_term = x_regs[0]; int local_process_id; - if (term_is_local_pid(recipient_term)) { + if (term_is_local_pid_or_port(recipient_term)) { local_process_id = term_to_local_process_id(recipient_term); } else if (term_is_atom(recipient_term)) { local_process_id = globalcontext_get_registered_process(ctx->global, term_to_atom_index(recipient_term)); @@ -2984,18 +2984,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #ifdef IMPL_EXECUTE_LOOP TRACE("is_port/2, label=%i, arg1=%lx\n", label, arg1); - if (term_is_local_pid(arg1)) { - int local_process_id = term_to_local_process_id(arg1); - Context *target = globalcontext_get_process_lock(ctx->global, local_process_id); - bool is_port_driver = false; - if (target) { - is_port_driver = context_is_port_driver(target); - globalcontext_get_process_unlock(ctx->global, target); - } - if (!is_port_driver) { - pc = mod->labels[label]; - } - } else { + if (!term_is_port(arg1)) { pc = mod->labels[label]; } #endif diff --git a/src/libAtomVM/term.c b/src/libAtomVM/term.c index b090e9d686..10a65a93ed 100644 --- a/src/libAtomVM/term.c +++ b/src/libAtomVM/term.c @@ -200,6 +200,16 @@ int term_funprint(PrinterFun *fun, term t, const GlobalContext *global) // creation is not printed return fun->print(fun, "<%" PRIu32 ".%" PRIu32 ".%" PRIu32 ">", node_atom_index, number, serial); + } else if (term_is_local_port(t)) { + int32_t process_id = term_to_local_process_id(t); + return fun->print(fun, "#Port<0.%" PRIu32 ".0>", process_id); + + } else if (term_is_external_port(t)) { + uint32_t node_atom_index = term_to_atom_index(term_to_external_node(t)); + uint64_t number = term_to_external_port_number(t); + // creation is not printed + return fun->print(fun, "#Port<%" PRIu32 ".%" PRIu64 ">", node_atom_index, number); + } else if (term_is_function(t)) { const term *boxed_value = term_to_const_term_ptr(t); @@ -447,24 +457,27 @@ static int term_type_to_index(term t) } else if (term_is_function(t)) { return 5; - } else if (term_is_pid(t)) { + } else if (term_is_port(t)) { return 6; - } else if (term_is_tuple(t)) { + } else if (term_is_pid(t)) { return 7; - } else if (term_is_nil(t)) { + } else if (term_is_tuple(t)) { return 8; - } else if (term_is_nonempty_list(t)) { + } else if (term_is_nil(t)) { return 9; - } else if (term_is_binary(t)) { + } else if (term_is_nonempty_list(t)) { return 10; - } else if (term_is_map(t)) { + } else if (term_is_binary(t)) { return 11; + } else if (term_is_map(t)) { + return 12; + } else { AVM_ABORT(); } diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 7a4b94cca0..b666e53117 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -45,8 +45,11 @@ extern "C" { #endif #define TERM_BOXED_VALUE_TAG 0x2 + +#define TERM_IMMED_TAG_MASK 0xF +#define TERM_PID_TAG 0x3 +#define TERM_PORT_TAG 0x7 #define TERM_INTEGER_TAG 0xF -#define TERM_CATCH_TAG 0x1B #define TERM_BOXED_TAG_MASK 0x3F #define TERM_BOXED_TUPLE 0x0 @@ -55,6 +58,7 @@ extern "C" { #define TERM_BOXED_REF 0x10 #define TERM_BOXED_FUN 0x14 #define TERM_BOXED_FLOAT 0x18 +#define TERM_CATCH_TAG 0x1B #define TERM_BOXED_REFC_BINARY 0x20 #define TERM_BOXED_HEAP_BINARY 0x24 #define TERM_BOXED_SUB_BINARY 0x28 @@ -80,6 +84,8 @@ extern "C" { #error #endif +#define TERM_MAX_LOCAL_PROCESS_ID ((1 << 28) - 1) + #define BINARY_HEADER_SIZE 2 #define FUNCTION_REFERENCE_SIZE 4 #define BOXED_INT_SIZE (BOXED_TERMS_REQUIRED_FOR_INT + 1) @@ -94,6 +100,7 @@ extern "C" { #else #error #endif +#define EXTERNAL_PORT_SIZE EXTERNAL_PID_SIZE #if TERM_BYTES == 8 #define EXTERNAL_REF_SIZE 5 #elif TERM_BYTES == 4 @@ -137,6 +144,15 @@ extern "C" { // "#Ref<" "." "." "." "." "." ">\0" (12 chars) #define EXTERNAL_REF_AS_CSTRING_LEN 70 +// 2^28-1 = 268435455 (9 chars) +// "#Port<0." ">\0" (10 chars) +#define LOCAL_PORT_AS_CSTRING_LEN 19 + +// 2^26-1 = 67108863 (8 chars) (node, atom index) +// 2^64-1 = 18446744073709551615 (20 chars) +// "#Port<" "." ">\0" (9 chars) +#define EXTERNAL_PORT_AS_CSTRING_LEN 37 + // 2^28-1 = 268435455 (9 chars) // "<0." ".0>\0" (7 chars) #define LOCAL_PID_AS_CSTRING_LEN 16 @@ -267,7 +283,7 @@ static inline const term *term_to_const_term_ptr(term t) static inline bool term_is_atom(term t) { /* atom: | atom index | 00 10 11 */ - return ((t & 0x3F) == 0xB); + return ((t & TERM_BOXED_TAG_MASK) == 0xB); } /** @@ -292,7 +308,7 @@ static inline bool term_is_invalid_term(term t) static inline bool term_is_nil(term t) { /* nil: 11 10 11 */ - return ((t & 0x3F) == 0x3B); + return ((t & TERM_BOXED_TAG_MASK) == 0x3B); } /** @@ -347,6 +363,8 @@ static inline size_t term_get_size_from_boxed_header(term header) switch (masked_value) { case TERM_BOXED_EXTERNAL_PID: return EXTERNAL_PID_SIZE - 1; + case TERM_BOXED_EXTERNAL_PORT: + return EXTERNAL_PORT_SIZE - 1; case TERM_BOXED_EXTERNAL_REF: return EXTERNAL_REF_SIZE - 1; default: @@ -450,7 +468,7 @@ static inline bool term_is_sub_binary(term t) static inline bool term_is_integer(term t) { /* integer: 11 11 */ - return ((t & 0xF) == 0xF); + return ((t & TERM_IMMED_TAG_MASK) == TERM_INTEGER_TAG); } /** @@ -462,7 +480,7 @@ static inline bool term_is_integer(term t) */ static inline bool term_is_uint8(term t) { - return ((t & ~((term) 0xFF0)) == 0xF); + return ((t & ~((term) 0xFF0)) == TERM_INTEGER_TAG); } static inline bool term_is_boxed_integer(term t) @@ -484,7 +502,7 @@ static inline bool term_is_any_integer(term t) static inline bool term_is_catch_label(term t) { - return (t & 0x3F) == TERM_CATCH_TAG; + return (t & TERM_BOXED_TAG_MASK) == TERM_CATCH_TAG; } /** @@ -497,7 +515,7 @@ static inline bool term_is_catch_label(term t) static inline bool term_is_local_pid(term t) { /* integer: 00 11 */ - return ((t & 0xF) == 0x3); + return ((t & TERM_IMMED_TAG_MASK) == TERM_PID_TAG); } /** @@ -511,7 +529,7 @@ static inline bool term_is_external_pid(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - if ((boxed_value[0] & 0x3F) == TERM_BOXED_EXTERNAL_PID) { + if ((boxed_value[0] & TERM_BOXED_TAG_MASK) == TERM_BOXED_EXTERNAL_PID) { return true; } } @@ -550,6 +568,19 @@ static inline bool term_is_pid(term t) return term_is_local_pid(t) || term_is_external_pid(t); } +/** + * @brief Checks if a term is a local port + * + * @details Returns \c true if a term is a local port, otherwise \c false. + * @param t the term that will be checked. + * @return \c true if check succeeds, \c false otherwise. + */ +static inline bool term_is_local_port(term t) +{ + /* integer: 01 11 */ + return ((t & TERM_IMMED_TAG_MASK) == TERM_PORT_TAG); +} + /** * @brief Checks if a term is an external port * @@ -561,7 +592,7 @@ static inline bool term_is_external_port(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - if ((boxed_value[0] & 0x3F) == TERM_BOXED_EXTERNAL_PORT) { + if ((boxed_value[0] & TERM_BOXED_TAG_MASK) == TERM_BOXED_EXTERNAL_PORT) { return true; } } @@ -569,6 +600,30 @@ static inline bool term_is_external_port(term t) return false; } +/** + * @brief Checks if a term is a port + * + * @details Returns \c true if a term is a port, otherwise \c false. + * @param t the term that will be checked. + * @return \c true if check succeeds, \c false otherwise. + */ +static inline bool term_is_port(term t) +{ + return term_is_local_port(t) || term_is_external_port(t); +} + +/** + * @brief Checks if a term is a local port or a local pid + * + * @details Returns \c true if a term is a local port or a local process id, otherwise \c false. + * @param t the term that will be checked. + * @return \c true if check succeeds, \c false otherwise. + */ +static inline bool term_is_local_pid_or_port(term t) +{ + return term_is_local_pid(t) || term_is_local_port(t); +} + /** * @brief Checks if a term is a tuple * @@ -580,7 +635,7 @@ static inline bool term_is_tuple(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - if ((boxed_value[0] & 0x3F) == TERM_BOXED_TUPLE) { + if ((boxed_value[0] & TERM_BOXED_TAG_MASK) == TERM_BOXED_TUPLE) { return true; } } @@ -599,7 +654,7 @@ static inline bool term_is_reference(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - const uint32_t header = boxed_value[0] & 0x3F; + const uint32_t header = boxed_value[0] & TERM_BOXED_TAG_MASK; if (header == TERM_BOXED_REF || header == TERM_BOXED_EXTERNAL_REF) { return true; } @@ -619,7 +674,7 @@ static inline bool term_is_local_reference(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - const uint32_t header = boxed_value[0] & 0x3F; + const uint32_t header = boxed_value[0] & TERM_BOXED_TAG_MASK; if (header == TERM_BOXED_REF) { return true; } @@ -639,7 +694,7 @@ static inline bool term_is_external_reference(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - const uint32_t header = boxed_value[0] & 0x3F; + const uint32_t header = boxed_value[0] & TERM_BOXED_TAG_MASK; if (header == TERM_BOXED_EXTERNAL_REF) { return true; } @@ -660,7 +715,7 @@ static inline bool term_is_function(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - if ((boxed_value[0] & 0x3F) == TERM_BOXED_FUN) { + if ((boxed_value[0] & TERM_BOXED_TAG_MASK) == TERM_BOXED_FUN) { return true; } } @@ -801,7 +856,7 @@ static inline int term_to_catch_label_and_module(term t, int *module_index) } /** - * @brief Gets process table index + * @brief Gets process table index for a local pid or port * * @details Returns local process table index for given atom term. * @param t the term that will be converted to local process table index, term type is checked. @@ -809,7 +864,7 @@ static inline int term_to_catch_label_and_module(term t, int *module_index) */ static inline int32_t term_to_local_process_id(term t) { - TERM_DEBUG_ASSERT(term_is_local_pid(t)); + TERM_DEBUG_ASSERT(term_is_local_pid(t) || term_is_local_port(t)); return t >> 4; } @@ -823,7 +878,7 @@ static inline int32_t term_to_local_process_id(term t) */ static inline term term_from_int4(int8_t value) { - return (value << 4) | 0xF; + return (value << 4) | TERM_INTEGER_TAG; } /** @@ -835,7 +890,7 @@ static inline term term_from_int4(int8_t value) */ static inline term term_from_int11(int16_t value) { - return (value << 4) | 0xF; + return (value << 4) | TERM_INTEGER_TAG; } /** @@ -855,11 +910,11 @@ static inline term term_from_int32(int32_t value) AVM_ABORT(); } else { - return (value << 4) | 0xF; + return (value << 4) | TERM_INTEGER_TAG; } #elif TERM_BITS == 64 - return (value << 4) | 0xF; + return (value << 4) | TERM_INTEGER_TAG; #else #error "Wrong TERM_BITS define" @@ -876,7 +931,7 @@ static inline term term_from_int64(int64_t value) AVM_ABORT(); } else { - return (value << 4) | 0xF; + return (value << 4) | TERM_INTEGER_TAG; } #elif TERM_BITS == 64 @@ -887,7 +942,7 @@ static inline term term_from_int64(int64_t value) AVM_ABORT(); } else { - return (value << 4) | 0xF; + return (value << 4) | TERM_INTEGER_TAG; } #else @@ -897,7 +952,7 @@ static inline term term_from_int64(int64_t value) static inline term term_from_int(avm_int_t value) { - return (value << 4) | 0xF; + return (value << 4) | TERM_INTEGER_TAG; } static inline avm_int_t term_unbox_int(term boxed_int) @@ -1027,7 +1082,19 @@ static inline term term_from_catch_label(unsigned int module_index, unsigned int */ static inline term term_from_local_process_id(uint32_t local_process_id) { - return (local_process_id << 4) | 0x3; + return (local_process_id << 4) | TERM_PID_TAG; +} + +/** + * @brief Port term from local process id + * + * @details Returns a term for a given local process table index. + * @param local_process_id the local process table index that will be converted to a term. + * @return a term that encapsulates a PID. + */ +static inline term term_port_from_local_process_id(uint32_t local_process_id) +{ + return (local_process_id << 4) | TERM_PORT_TAG; } /** @@ -1365,6 +1432,37 @@ static inline term term_from_external_process_id(term node, uint32_t process_id, return ((term) boxed_value) | TERM_BOXED_VALUE_TAG; } +/** + * @brief Get a port term from node, number and creation + * + * @param node name of the node (atom) + * @param number port number on that node + * @param creation creation of that node + * @param heap the heap to allocate memory in + * @return an external heap term created using given parameters. + */ +static inline term term_from_external_port_number(term node, uint64_t number, uint32_t creation, Heap *heap) +{ + term *boxed_value = memory_heap_alloc(heap, EXTERNAL_PORT_SIZE); + int atom_index = term_to_atom_index(node); + boxed_value[0] = (atom_index << 6) | TERM_BOXED_EXTERNAL_PORT; + + #if TERM_BYTES == 8 + boxed_value[1] = (term) creation; + boxed_value[2] = (term) number; + + #elif TERM_BYTES == 4 + boxed_value[1] = (term) creation; + boxed_value[2] = (term) (uint32_t) (number >> 32); + boxed_value[3] = (term) (uint32_t) number; + + #else + #error "terms must be either 32 or 64 bit wide" + #endif + + return ((term) boxed_value) | TERM_BOXED_VALUE_TAG; +} + /** * @brief Get the name of a node for a given external thing * @@ -1441,6 +1539,30 @@ static inline uint32_t term_to_external_pid_serial(term t) #endif } +/** + * @brief Get the port number of an external port + * + * @param term external port + * @return the port number of the external port + */ +static inline uint64_t term_to_external_port_number(term t) +{ + TERM_DEBUG_ASSERT(term_is_external_port(t)); + + const term *boxed_value = term_to_const_term_ptr(t); + + #if TERM_BYTES == 8 + return (uint64_t) boxed_value[2]; + + #elif TERM_BYTES == 4 + return (((uint64_t) boxed_value[2]) << 32) | (uint64_t) boxed_value[3]; + + #else + #error "terms must be either 32 or 64 bit wide" + #endif +} + + /** * @brief Get a reference term from node, creation, number of words and words * @@ -1569,7 +1691,7 @@ static inline void term_put_tuple_element(term t, uint32_t elem_index, term put_ term *boxed_value = term_to_term_ptr(t); - TERM_DEBUG_ASSERT(((boxed_value[0] & 0x3F) == 0) && (elem_index < (boxed_value[0] >> 6))); + TERM_DEBUG_ASSERT(((boxed_value[0] & TERM_BOXED_TAG_MASK) == 0) && (elem_index < (boxed_value[0] >> 6))); boxed_value[elem_index + 1] = put_value; } @@ -1588,7 +1710,7 @@ static inline term term_get_tuple_element(term t, int elem_index) const term *boxed_value = term_to_const_term_ptr(t); - TERM_DEBUG_ASSERT(((boxed_value[0] & 0x3F) == 0) && (elem_index < (boxed_value[0] >> 6))); + TERM_DEBUG_ASSERT(((boxed_value[0] & TERM_BOXED_TAG_MASK) == 0) && (elem_index < (boxed_value[0] >> 6))); return boxed_value[elem_index + 1]; } @@ -1865,7 +1987,7 @@ static inline bool term_is_match_state(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - if ((boxed_value[0] & 0x3F) == TERM_BOXED_BIN_MATCH_STATE) { + if ((boxed_value[0] & TERM_BOXED_TAG_MASK) == TERM_BOXED_BIN_MATCH_STATE) { return true; } } diff --git a/src/platforms/esp32/components/avm_builtins/socket_driver.c b/src/platforms/esp32/components/avm_builtins/socket_driver.c index 585c02a2e1..6cec74abb8 100644 --- a/src/platforms/esp32/components/avm_builtins/socket_driver.c +++ b/src/platforms/esp32/components/avm_builtins/socket_driver.c @@ -515,7 +515,7 @@ static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, uint Context *new_ctx = context_new(glb); new_ctx->native_handler = socket_consume_mailbox; - term socket_pid = term_from_local_process_id(new_ctx->process_id); + term socket_pid = term_port_from_local_process_id(new_ctx->process_id); struct TCPClientSocketData *new_tcp_data = tcp_client_socket_data_new(new_ctx, accepted_conn, sockets, pid); socket_data_postinit(platform); @@ -585,7 +585,7 @@ static void do_send_socket_error(Context *ctx, err_t status) term reason_atom = lwip_error_atom(glb, status); term result_tuple = term_alloc_tuple(3, &ctx->heap); term_put_tuple_element(result_tuple, 0, globalcontext_make_atom(glb, tcp_error_atom)); - term socket_pid = term_from_local_process_id(ctx->process_id); + term socket_pid = term_port_from_local_process_id(ctx->process_id); term socket_wrapper = create_tcp_socket_wrapper(socket_pid, &ctx->heap, glb); term_put_tuple_element(result_tuple, 1, socket_wrapper); term_put_tuple_element(result_tuple, 2, reason_atom); @@ -609,7 +609,7 @@ static void do_send_tcp_closed(Context *ctx) } term result_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(result_tuple, 0, TCP_CLOSED_ATOM); - term socket_pid = term_from_local_process_id(ctx->process_id); + term socket_pid = term_port_from_local_process_id(ctx->process_id); term socket_wrapper = create_tcp_socket_wrapper(socket_pid, &ctx->heap, glb); term_put_tuple_element(result_tuple, 1, socket_wrapper); globalcontext_send_message(glb, socket_data->controlling_process_pid, result_tuple); @@ -750,7 +750,7 @@ static NativeHandlerResult do_receive_data(Context *ctx) if (socket_data->active) { term active_tuple = term_alloc_tuple(socket_data->type == TCPClientSocket ? 3 : 5, &ctx->heap); term_put_tuple_element(active_tuple, 0, socket_data->type == TCPClientSocket ? TCP_ATOM : UDP_ATOM); - term socket_pid = term_from_local_process_id(ctx->process_id); + term socket_pid = term_port_from_local_process_id(ctx->process_id); term socket_wrapper = socket_data->type == UDPSocket ? create_udp_socket_wrapper(socket_pid, &ctx->heap, ctx->global) : diff --git a/src/platforms/esp32/test/main/test_erl_sources/test_socket.erl b/src/platforms/esp32/test/main/test_erl_sources/test_socket.erl index 29b80d2489..12974b037c 100644 --- a/src/platforms/esp32/test/main/test_erl_sources/test_socket.erl +++ b/src/platforms/esp32/test/main/test_erl_sources/test_socket.erl @@ -217,7 +217,7 @@ test_tcp_server(Active, Port) -> link(Pid), {ok, ConnectedSocket} = case call(ServerSocket, {accept, 30000}) of - {ok, Socket} when is_pid(Socket) -> + {ok, Socket} when is_port(Socket) -> {ok, Socket}; X -> {unexpected_accept, X} diff --git a/src/platforms/generic_unix/lib/socket_driver.c b/src/platforms/generic_unix/lib/socket_driver.c index ebf52e83dc..b582ad1ff1 100644 --- a/src/platforms/generic_unix/lib/socket_driver.c +++ b/src/platforms/generic_unix/lib/socket_driver.c @@ -722,7 +722,7 @@ static EventListener *active_recv_callback(GlobalContext *glb, EventListener *ba // {tcp_closed, {Moniker :: atom(), Socket :: pid(), Module :: module()}} BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + TUPLE_SIZE(3), heap); term pid = socket_data->controlling_process; - term socket_pid = term_from_local_process_id(ctx->process_id); + term socket_pid = term_port_from_local_process_id(ctx->process_id); term socket_wrapper = create_tcp_socket_wrapper(socket_pid, &heap, glb); term msgs[2] = { TCP_CLOSED_ATOM, socket_wrapper }; term msg = port_heap_create_tuple_n(&heap, 2, msgs); @@ -749,7 +749,7 @@ static EventListener *active_recv_callback(GlobalContext *glb, EventListener *ba } term pid = socket_data->controlling_process; term packet = socket_create_packet_term(buf, len, socket_data->binary, &heap, glb); - term socket_pid = term_from_local_process_id(ctx->process_id); + term socket_pid = term_port_from_local_process_id(ctx->process_id); term socket_wrapper = create_tcp_socket_wrapper(socket_pid, &heap, glb); term msgs[3] = { TCP_ATOM, socket_wrapper, packet }; term msg = port_heap_create_tuple_n(&heap, 3, msgs); @@ -868,7 +868,7 @@ static EventListener *active_recvfrom_callback(GlobalContext *glb, EventListener // {udp, {Moniker :: atom(), Socket :: pid(), Module :: module()}, {error, {SysCall, Errno}}} BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(3) + TUPLE_SIZE(3) + TUPLE_SIZE(2) + TUPLE_SIZE(2), heap); term pid = socket_data->controlling_process; - term socket_pid = term_from_local_process_id(ctx->process_id); + term socket_pid = term_port_from_local_process_id(ctx->process_id); // printf("Sending tcp_closed wrapper to %i\n", ctx->process_id); term socket_wrapper = create_udp_socket_wrapper(socket_pid, &heap, glb); term msgs[3] = { UDP_ATOM, socket_wrapper, port_heap_create_sys_error_tuple(&heap, RECVFROM_ATOM, errno) }; @@ -894,7 +894,7 @@ static EventListener *active_recvfrom_callback(GlobalContext *glb, EventListener term addr = socket_heap_tuple_from_addr(&heap, htonl(clientaddr.sin_addr.s_addr)); term port = term_from_int32(htons(clientaddr.sin_port)); term packet = socket_create_packet_term(buf, len, socket_data->binary, &heap, glb); - term socket_pid = term_from_local_process_id(ctx->process_id); + term socket_pid = term_port_from_local_process_id(ctx->process_id); term socket_wrapper = create_udp_socket_wrapper(socket_pid, &heap, glb); term msgs[5] = { UDP_ATOM, socket_wrapper, addr, port, packet }; term msg = port_heap_create_tuple_n(&heap, 5, msgs); @@ -1072,7 +1072,7 @@ static EventListener *accept_callback(GlobalContext *glb, EventListener *base_li } // {Ref, Socket} - term socket_pid = term_from_local_process_id(new_ctx->process_id); + term socket_pid = term_port_from_local_process_id(new_ctx->process_id); BEGIN_WITH_STACK_HEAP(10, heap); term ref = term_from_ref_ticks(listener->ref_ticks, &heap); term payload = port_heap_create_ok_tuple(&heap, socket_pid); diff --git a/tests/erlang_tests/test_binary_to_term.erl b/tests/erlang_tests/test_binary_to_term.erl index c1281633c8..f9e46c8948 100644 --- a/tests/erlang_tests/test_binary_to_term.erl +++ b/tests/erlang_tests/test_binary_to_term.erl @@ -29,6 +29,7 @@ get_binary/1, test_atom_decoding_checks/0, test_encode_pid/0, + test_encode_port/0, id/1 ]). @@ -175,6 +176,7 @@ start() -> ok = test_atom_decoding_checks(), ok = test_encode_pid(), ok = test_encode_reference(), + ok = test_encode_port(), 0. test_reverse(T, Interop) -> @@ -528,6 +530,74 @@ do_unsetnode({NetKernelPid, MonitorRef}) -> end, ok. +test_encode_port() -> + TestPort = open_port({spawn, "echo"}, []), + Bin = term_to_binary(TestPort), + TestPort = binary_to_term(Bin), + true = is_port(TestPort), + {ExpectedSize, SupportsV4PortEncoding} = + case erlang:system_info(machine) of + "ATOM" -> + % small utf8 atom + {29, true}; + "BEAM" -> + OTPRelease = erlang:system_info(otp_release), + if + OTPRelease < "23" -> {23, false}; + OTPRelease < "24" -> {26, false}; + % v4 is supported but not the default + OTPRelease < "26" -> {26, true}; + % small utf8 atom + true -> {29, true} + end + end, + ExpectedSize = byte_size(Bin), + case SupportsV4PortEncoding of + true -> + true = is_port( + binary_to_term( + <<131, 120, 119, 13, "nonode@nohost", 1:64, 0:32>> + ) + ), + Port1 = binary_to_term(<<131, 120, 119, 4, "true", 43:64, 0:32>>), + Port2 = binary_to_term(<<131, 120, 119, 4, "true", 43:64, 1:32>>), + false = Port1 =:= Port2, + "#Port<1.43>" = port_to_list(Port1), + "#Port<1.43>" = port_to_list(Port2), + ok; + false -> + ok + end, + case has_setnode_creation() of + true -> + % Test distributed ports + Ref42 = do_setnode(test@test_node, 1042), + DistributedBin42 = term_to_binary(TestPort), + true = DistributedBin42 =/= Bin, + TestRef42 = binary_to_term(DistributedBin42), + true = TestRef42 =:= TestPort, + ExpectedSize = byte_size(DistributedBin42) - 1, + + ok = do_unsetnode(Ref42), + + Ref43 = do_setnode(test@test_node, 1043), + DistributedBin43 = term_to_binary(TestPort), + true = DistributedBin43 =/= DistributedBin42, + TestRef43 = binary_to_term(DistributedBin43), + true = TestRef43 =:= TestPort, + ExpectedSize = byte_size(DistributedBin43) - 1, + + % If our creation is 1043, encoded binary with creation 1042 is a different port + TestRef42_43 = binary_to_term(DistributedBin42), + false = TestRef42_43 =:= TestPort, + + ok = do_unsetnode(Ref43), + ok; + false -> + ok + end, + ok. + test_encode_reference() -> TestRef = make_ref(), Bin = term_to_binary(TestRef),