Skip to content

Commit 3044560

Browse files
committed
Forward port changes from v0.6 release branch
Merge is_record BIF (PR atomvm#1564) and fixes (such as PR atomvm#1615 and atomvm#1617) from release-0.6 branch.
2 parents ca3f7c7 + 74fe76e commit 3044560

File tree

19 files changed

+217
-55
lines changed

19 files changed

+217
-55
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ with nodejs and emscripten)
4545
- Added documentation and function specs for uart driver
4646
- Added `uart:read/2` with a timeout parameter.
4747
- Missing `erlang:is_function/2` BIF
48+
- Added `erlang:is_record/2`
4849

4950
### Fixed
5051

libs/estdlib/src/erlang.erl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
md5/1,
3838
is_map/1,
3939
is_map_key/2,
40+
is_record/2,
4041
map_size/1,
4142
map_get/2,
4243
monotonic_time/1,
@@ -405,6 +406,17 @@ apply(Function, Args) ->
405406
is_map(_Map) ->
406407
erlang:nif_error(undefined).
407408

409+
%%-----------------------------------------------------------------------------
410+
%% @param Term
411+
%% @param RecordTag atom representing tuple tag
412+
%%
413+
%% @doc Returns true if Term is a tuple and its first element is RecordTag, false otherwise.
414+
%% @end
415+
%%-----------------------------------------------------------------------------
416+
-spec is_record(Term :: term(), RecordTag :: atom()) -> boolean().
417+
is_record(_Term, _RecordTag) ->
418+
erlang:nif_error(undefined).
419+
408420
%%-----------------------------------------------------------------------------
409421
%% @param Map the map
410422
%% @returns the size of the map

src/libAtomVM/bif.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,18 @@ term bif_erlang_is_tuple_1(Context *ctx, uint32_t fail_label, term arg1)
245245
return term_is_tuple(arg1) ? TRUE_ATOM : FALSE_ATOM;
246246
}
247247

248+
term bif_erlang_is_record_2(Context *ctx, uint32_t fail_label, term arg1, term arg2)
249+
{
250+
UNUSED(ctx);
251+
VALIDATE_VALUE_BIF(fail_label, arg2, term_is_atom);
252+
if (!term_is_tuple(arg1) || term_get_tuple_arity(arg1) == 0) {
253+
return FALSE_ATOM;
254+
}
255+
256+
term tag = term_get_tuple_element(arg1, 0);
257+
return tag == arg2 ? TRUE_ATOM : FALSE_ATOM;
258+
}
259+
248260
term bif_erlang_is_map_1(Context *ctx, uint32_t fail_label, term arg1)
249261
{
250262
UNUSED(ctx);

src/libAtomVM/bif.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ term bif_erlang_is_number_1(Context *ctx, uint32_t fail_label, term arg1);
5858
term bif_erlang_is_pid_1(Context *ctx, uint32_t fail_label, term arg1);
5959
term bif_erlang_is_reference_1(Context *ctx, uint32_t fail_label, term arg1);
6060
term bif_erlang_is_tuple_1(Context *ctx, uint32_t fail_label, term arg1);
61+
term bif_erlang_is_record_2(Context *ctx, uint32_t fail_label, term arg1, term record_tag);
6162
term bif_erlang_is_map_1(Context *ctx, uint32_t fail_label, term arg1);
6263
term bif_erlang_is_map_key_2(Context *ctx, uint32_t fail_label, term arg1, term arg2);
6364

src/libAtomVM/bifs.gperf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ erlang:is_number/1, {.bif.base.type = BIFFunctionType, .bif.bif1_ptr = bif_erlan
5353
erlang:is_pid/1, {.bif.base.type = BIFFunctionType, .bif.bif1_ptr = bif_erlang_is_pid_1}
5454
erlang:is_reference/1, {.bif.base.type = BIFFunctionType, .bif.bif1_ptr = bif_erlang_is_reference_1}
5555
erlang:is_tuple/1, {.bif.base.type = BIFFunctionType, .bif.bif1_ptr = bif_erlang_is_tuple_1}
56+
erlang:is_record/2,{.bif.base.type = BIFFunctionType, .bif.bif2_ptr = bif_erlang_is_record_2}
5657
erlang:is_map/1, {.bif.base.type = BIFFunctionType, .bif.bif1_ptr = bif_erlang_is_map_1}
5758
erlang:is_map_key/2, {.bif.base.type = BIFFunctionType, .bif.bif2_ptr = bif_erlang_is_map_key_2}
5859
erlang:not/1, {.bif.base.type = BIFFunctionType, .bif.bif1_ptr = bif_erlang_not_1}

src/libAtomVM/context.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,6 @@ struct Context
119119
// Ports support
120120
native_handler_f native_handler;
121121

122-
uint64_t reductions;
123-
124122
unsigned int leader : 1;
125123
unsigned int has_min_heap_size : 1;
126124
unsigned int has_max_heap_size : 1;

src/libAtomVM/erl_nif.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@ ErlNifResourceType *enif_init_resource_type(ErlNifEnv *env, const char *name, co
150150

151151
/**
152152
* @brief Allocate a resource for given type for `size` bytes.
153+
* @details following BEAM semantics, the resource is created with a refcount
154+
* of 1. A call to `enif_release_resource` will decrement the refcount and
155+
* destroy the resource if it is zero.
156+
*
157+
* Typical usage (as suggested by BEAM documentation) is to call
158+
* `enif_make_resource` and then `enif_release_resource` to somewhat transfer
159+
* ownership to the garbage collector. `enif_make_resource` will increment
160+
* refcount to 2 and also add the resource to the MSO list of the context, so
161+
* when the term is no longer referenced in the context heap, the reference
162+
* counter will be decremented by gc.
163+
*
153164
* @param type a trype created by `enif_init_resource_type`.
154165
* @param size the size in bytes.
155166
* @return a pointer or `NULL` on failure.
@@ -184,9 +195,18 @@ int enif_release_resource(void *resource);
184195
/**
185196
* @brief create a term from a resource
186197
* @details the term can be later passed to `enif_get_resource`.
187-
* The resource is typically released (by calling `enif_release_resource`)
188-
* just after calling this function to "transfer ownership" to Erlang code so
189-
* that it will be destroyed when garbage collected.
198+
*
199+
* The resource reference counter is incremented and it is added to the MSO
200+
* list of the heap of env (which must be a context).
201+
*
202+
* If the resource was just allocated with `enif_alloc_resource`, the reference
203+
* counter should typically be decremented by a call to `enif_release_resource`
204+
* matching usage documented by BEAM.
205+
*
206+
* If the resource was not just allocated with `enif_alloc_resource`, to clear
207+
* usage confusion, users should rather call `term_from_resource` and should
208+
* not decrement the reference counter.
209+
*
190210
* @param env current environment
191211
* @param obj resource
192212
* @return a new term representing the resource

src/libAtomVM/opcodesswitch.h

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4832,7 +4832,6 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
48324832
}
48334833
#endif
48344834

4835-
#if MINIMUM_OTP_COMPILER_VERSION <= 25
48364835
case OP_BS_MATCH_STRING: {
48374836
uint32_t fail;
48384837
DECODE_LABEL(fail, pc)
@@ -4853,58 +4852,67 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
48534852
avm_int_t bs_offset = term_get_match_state_offset(src);
48544853
term bs_bin = term_get_match_state_binary(src);
48554854

4855+
TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);
4856+
48564857
size_t remaining = 0;
48574858
const uint8_t *str = module_get_str(mod, offset, &remaining);
48584859
if (IS_NULL_PTR(str)) {
48594860
TRACE("bs_match_string: Bad offset in strings table.\n");
48604861
RAISE_ERROR(BADARG_ATOM);
48614862
}
48624863

4863-
TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);
4864-
4865-
if (bits % 8 == 0 && bs_offset % 8 == 0) {
4866-
avm_int_t bytes = bits / 8;
4867-
avm_int_t byte_offset = bs_offset / 8;
4868-
4869-
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
4870-
TRACE("bs_match_string: failed to match\n");
4871-
JUMP_TO_ADDRESS(mod->labels[fail]);
4872-
}
4864+
if (term_binary_size(bs_bin) * 8 - bs_offset < MIN(remaining * 8, bits)) {
4865+
TRACE("bs_match_string: failed to match (binary is shorter)\n");
4866+
JUMP_TO_ADDRESS(mod->labels[fail]);
48734867
} else {
4874-
// Compare unaligned bits
4875-
const uint8_t *bs_str = (const uint8_t *) term_binary_data(bs_bin) + (bs_offset / 8);
4876-
uint8_t bin_bit_offset = 7 - (bs_offset - (8 *(bs_offset / 8)));
4877-
uint8_t str_bit_offset = 7;
4878-
size_t remaining_bits = bits;
4879-
while (remaining_bits > 0) {
4880-
uint8_t str_ch = *str;
4881-
uint8_t bin_ch = *bs_str;
4882-
uint8_t str_ch_bit = (str_ch >> str_bit_offset) & 1;
4883-
uint8_t bin_ch_bit = (bin_ch >> bin_bit_offset) & 1;
4884-
if (str_ch_bit ^ bin_ch_bit) {
4868+
if (bits % 8 == 0 && bs_offset % 8 == 0) {
4869+
avm_int_t bytes = bits / 8;
4870+
avm_int_t byte_offset = bs_offset / 8;
4871+
4872+
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
48854873
TRACE("bs_match_string: failed to match\n");
48864874
JUMP_TO_ADDRESS(mod->labels[fail]);
4887-
}
4888-
if (str_bit_offset) {
4889-
str_bit_offset--;
48904875
} else {
4891-
str_bit_offset = 7;
4892-
str++;
4876+
term_set_match_state_offset(src, bs_offset + bits);
48934877
}
4894-
if (bin_bit_offset) {
4895-
bin_bit_offset--;
4896-
} else {
4897-
bin_bit_offset = 7;
4898-
bs_str++;
4878+
} else {
4879+
// Compare unaligned bits
4880+
const uint8_t *bs_str = (const uint8_t *) term_binary_data(bs_bin) + (bs_offset / 8);
4881+
uint8_t bin_bit_offset = 7 - (bs_offset - (8 *(bs_offset / 8)));
4882+
uint8_t str_bit_offset = 7;
4883+
size_t remaining_bits = bits;
4884+
while (remaining_bits > 0) {
4885+
uint8_t str_ch = *str;
4886+
uint8_t bin_ch = *bs_str;
4887+
uint8_t str_ch_bit = (str_ch >> str_bit_offset) & 1;
4888+
uint8_t bin_ch_bit = (bin_ch >> bin_bit_offset) & 1;
4889+
if (str_ch_bit ^ bin_ch_bit) {
4890+
TRACE("bs_match_string: failed to match\n");
4891+
JUMP_TO_ADDRESS(mod->labels[fail]);
4892+
break;
4893+
}
4894+
if (str_bit_offset) {
4895+
str_bit_offset--;
4896+
} else {
4897+
str_bit_offset = 7;
4898+
str++;
4899+
}
4900+
if (bin_bit_offset) {
4901+
bin_bit_offset--;
4902+
} else {
4903+
bin_bit_offset = 7;
4904+
bs_str++;
4905+
}
4906+
remaining_bits--;
4907+
}
4908+
if (remaining_bits == 0) {
4909+
term_set_match_state_offset(src, bs_offset + bits);
48994910
}
4900-
remaining_bits--;
49014911
}
49024912
}
4903-
term_set_match_state_offset(src, bs_offset + bits);
49044913
#endif
49054914
break;
49064915
}
4907-
#endif
49084916

49094917
#if MINIMUM_OTP_COMPILER_VERSION <= 21
49104918
case OP_BS_SAVE2: {

src/libAtomVM/otp_socket.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
530530
#ifndef AVM_NO_SMP
531531
rsrc_obj->socket_lock = smp_rwlock_create();
532532
if (IS_NULL_PTR(rsrc_obj->socket_lock)) {
533+
// destroy resource object without calling dtor
533534
free(rsrc_obj);
534535
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
535536
}
@@ -538,6 +539,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
538539
rsrc_obj->fd = socket(domain, type, protocol);
539540
if (UNLIKELY(rsrc_obj->fd == -1 || rsrc_obj->fd == CLOSED_FD)) {
540541
AVM_LOGE(TAG, "Failed to initialize socket.");
542+
rsrc_obj->fd = CLOSED_FD;
541543
enif_release_resource(rsrc_obj);
542544
return make_errno_tuple(ctx);
543545
} else {
@@ -556,6 +558,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
556558
LWIP_END();
557559
rsrc_obj->socket_state = SocketStateUDPIdle;
558560
} else {
561+
rsrc_obj->socket_state = SocketStateClosed;
559562
enif_release_resource(rsrc_obj);
560563
RAISE_ERROR(BADARG_ATOM);
561564
}
@@ -587,7 +590,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
587590
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
588591
}
589592
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
590-
enif_release_resource(rsrc_obj);
593+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
591594

592595
size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE;
593596
if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
@@ -789,6 +792,7 @@ static struct SocketResource *make_accepted_socket_resource(struct tcp_pcb *newp
789792
#ifndef AVM_NO_SMP
790793
conn_rsrc_obj->socket_lock = smp_rwlock_create();
791794
if (IS_NULL_PTR(conn_rsrc_obj->socket_lock)) {
795+
// destroy resource without calling destructor
792796
free(conn_rsrc_obj);
793797
return NULL;
794798
}
@@ -1653,7 +1657,7 @@ static term nif_socket_listen(Context *ctx, int argc, term argv[])
16531657
static term make_accepted_socket_term(Context *ctx, struct SocketResource *conn_rsrc_obj)
16541658
{
16551659
term obj = enif_make_resource(erl_nif_env_from_context(ctx), conn_rsrc_obj);
1656-
enif_release_resource(conn_rsrc_obj);
1660+
enif_release_resource(conn_rsrc_obj); // decrement refcount after enif_allocate_resource in make_accepted_socket_resource
16571661

16581662
term socket_term = term_alloc_tuple(2, &ctx->heap);
16591663
uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global);
@@ -1726,7 +1730,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[])
17261730
TRACE("nif_socket_accept: Created socket on accept fd=%i\n", rsrc_obj->fd);
17271731

17281732
term new_resource = enif_make_resource(erl_nif_env_from_context(ctx), conn_rsrc_obj);
1729-
enif_release_resource(conn_rsrc_obj);
1733+
enif_release_resource(conn_rsrc_obj); // decrement refcount after enif_alloc_resource
17301734

17311735
size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE;
17321736
if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &new_resource, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
@@ -1763,6 +1767,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[])
17631767
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
17641768
LWIP_END();
17651769
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
1770+
enif_release_resource(new_resource);
17661771
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
17671772
}
17681773

src/libAtomVM/otp_ssl.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ static term nif_ssl_entropy_init(Context *ctx, int argc, term argv[])
236236
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
237237
}
238238
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
239-
enif_release_resource(rsrc_obj);
239+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
240240

241241
mbedtls_entropy_init(&rsrc_obj->context);
242242

@@ -259,7 +259,7 @@ static term nif_ssl_ctr_drbg_init(Context *ctx, int argc, term argv[])
259259
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
260260
}
261261
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
262-
enif_release_resource(rsrc_obj);
262+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
263263

264264
mbedtls_ctr_drbg_init(&rsrc_obj->context);
265265

@@ -311,7 +311,7 @@ static term nif_ssl_init(Context *ctx, int argc, term argv[])
311311
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
312312
}
313313
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
314-
enif_release_resource(rsrc_obj);
314+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
315315

316316
mbedtls_ssl_init(&rsrc_obj->context);
317317

@@ -364,7 +364,7 @@ static term nif_ssl_config_init(Context *ctx, int argc, term argv[])
364364
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
365365
}
366366
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
367-
enif_release_resource(rsrc_obj);
367+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
368368

369369
mbedtls_ssl_config_init(&rsrc_obj->config);
370370

0 commit comments

Comments
 (0)