diff --git a/.github/workflows/build-and-test-macos.yaml b/.github/workflows/build-and-test-macos.yaml index 0035b89680..fc3b58d983 100644 --- a/.github/workflows/build-and-test-macos.yaml +++ b/.github/workflows/build-and-test-macos.yaml @@ -10,7 +10,11 @@ on: [push, pull_request] jobs: build-and-test: - runs-on: "macos-11" + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: ["macos-11"] + otp: ["23", "24", "25"] steps: # Setup @@ -20,7 +24,7 @@ jobs: submodules: 'recursive' - name: "Install deps" - run: brew install gperf doxygen erlang@21 ninja + run: brew install gperf doxygen erlang@${{ matrix.otp }} ninja # Builder info - name: "System info" @@ -40,13 +44,13 @@ jobs: - name: "Build: run cmake" working-directory: build run: | - export PATH="/usr/local/opt/erlang@21/bin:$PATH" + export PATH="/usr/local/opt/erlang@${{ matrix.otp }}/bin:$PATH" cmake -G Ninja -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl .. - name: "Build: run ninja" working-directory: build run: | - export PATH="/usr/local/opt/erlang@21/bin:$PATH" + export PATH="/usr/local/opt/erlang@${{ matrix.otp }}/bin:$PATH" ninja # Test diff --git a/.github/workflows/build-and-test-other.yaml b/.github/workflows/build-and-test-other.yaml index 59a5c4a508..8ea742e079 100644 --- a/.github/workflows/build-and-test-other.yaml +++ b/.github/workflows/build-and-test-other.yaml @@ -9,8 +9,8 @@ name: Build and Test on Other Architectures on: [push, pull_request] env: - otp_version: 21 - elixir_version: 1.9 + otp_version: 24 + elixir_version: 1.14 jobs: build-and-test-other: diff --git a/src/libAtomVM/defaultatoms.c b/src/libAtomVM/defaultatoms.c index 0de0c773bc..c31a87fcad 100644 --- a/src/libAtomVM/defaultatoms.c +++ b/src/libAtomVM/defaultatoms.c @@ -103,6 +103,18 @@ static const char *const timeout_value_atom = "\xD" "timeout_value"; static const char *const machine_atom = "\x7" "machine"; static const char *const avm_floatsize_atom = "\xD" "avm_floatsize"; +static const char *const append_atom = "\x6" "append"; +static const char *const private_append_atom = "\xE" "private_append"; +static const char *const binary_atom = "\x6" "binary"; +static const char *const integer_atom = "\x7" "integer"; +static const char *const little_atom = "\x6" "little"; +static const char *const native_atom = "\x6" "native"; +static const char *const string_atom = "\x6" "string"; +static const char *const utf8_atom = "\x4" "utf8"; +static const char *const utf16_atom = "\x5" "utf16"; +static const char *const utf32_atom = "\x5" "utf32"; +static const char *const badrecord_atom = "\x9" "badrecord"; + void defaultatoms_init(GlobalContext *glb) { int ok = 1; @@ -190,6 +202,18 @@ void defaultatoms_init(GlobalContext *glb) ok &= globalcontext_insert_atom(glb, machine_atom) == MACHINE_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, avm_floatsize_atom) == AVM_FLOATSIZE_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, append_atom) == APPEND_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, private_append_atom) == PRIVATE_APPEND_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, binary_atom) == BINARY_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, integer_atom) == INTEGER_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, little_atom) == LITTLE_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, native_atom) == NATIVE_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, string_atom) == STRING_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, utf8_atom) == UTF8_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, utf16_atom) == UTF16_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, utf32_atom) == UTF32_ATOM_INDEX; + ok &= globalcontext_insert_atom(glb, badrecord_atom) == BADRECORD_ATOM_INDEX; + if (!ok) { AVM_ABORT(); } diff --git a/src/libAtomVM/defaultatoms.h b/src/libAtomVM/defaultatoms.h index 9093d54708..cda64125e6 100644 --- a/src/libAtomVM/defaultatoms.h +++ b/src/libAtomVM/defaultatoms.h @@ -112,7 +112,19 @@ extern "C" { #define MACHINE_ATOM_INDEX 65 #define AVM_FLOATSIZE_ATOM_INDEX 66 -#define PLATFORM_ATOMS_BASE_INDEX 67 +#define APPEND_ATOM_INDEX 67 +#define PRIVATE_APPEND_ATOM_INDEX 68 +#define BINARY_ATOM_INDEX 69 +#define INTEGER_ATOM_INDEX 70 +#define LITTLE_ATOM_INDEX 71 +#define NATIVE_ATOM_INDEX 72 +#define STRING_ATOM_INDEX 73 +#define UTF8_ATOM_INDEX 74 +#define UTF16_ATOM_INDEX 75 +#define UTF32_ATOM_INDEX 76 +#define BADRECORD_ATOM_INDEX 77 + +#define PLATFORM_ATOMS_BASE_INDEX 78 #define FALSE_ATOM TERM_FROM_ATOM_INDEX(FALSE_ATOM_INDEX) #define TRUE_ATOM TERM_FROM_ATOM_INDEX(TRUE_ATOM_INDEX) @@ -120,7 +132,7 @@ extern "C" { #define OK_ATOM term_from_atom_index(OK_ATOM_INDEX) #define ERROR_ATOM term_from_atom_index(ERROR_ATOM_INDEX) -#define UNDEFINED_ATOM term_from_atom_index(UNDEFINED_ATOM_INDEX) +#define UNDEFINED_ATOM TERM_FROM_ATOM_INDEX(UNDEFINED_ATOM_INDEX) #define BADARG_ATOM term_from_atom_index(BADARG_ATOM_INDEX) #define BADARITH_ATOM term_from_atom_index(BADARITH_ATOM_INDEX) @@ -199,6 +211,18 @@ extern "C" { #define MACHINE_ATOM TERM_FROM_ATOM_INDEX(MACHINE_ATOM_INDEX) #define AVM_FLOATSIZE_ATOM TERM_FROM_ATOM_INDEX(AVM_FLOATSIZE_ATOM_INDEX) +#define APPEND_ATOM TERM_FROM_ATOM_INDEX(APPEND_ATOM_INDEX) +#define PRIVATE_APPEND_ATOM TERM_FROM_ATOM_INDEX(PRIVATE_APPEND_ATOM_INDEX) +#define BINARY_ATOM TERM_FROM_ATOM_INDEX(BINARY_ATOM_INDEX) +#define INTEGER_ATOM TERM_FROM_ATOM_INDEX(INTEGER_ATOM_INDEX) +#define LITTLE_ATOM TERM_FROM_ATOM_INDEX(LITTLE_ATOM_INDEX) +#define NATIVE_ATOM TERM_FROM_ATOM_INDEX(NATIVE_ATOM_INDEX) +#define STRING_ATOM TERM_FROM_ATOM_INDEX(STRING_ATOM_INDEX) +#define UTF8_ATOM TERM_FROM_ATOM_INDEX(UTF8_ATOM_INDEX) +#define UTF16_ATOM TERM_FROM_ATOM_INDEX(UTF16_ATOM_INDEX) +#define UTF32_ATOM TERM_FROM_ATOM_INDEX(UTF32_ATOM_INDEX) +#define BADRECORD_ATOM TERM_FROM_ATOM_INDEX(BADRECORD_ATOM_INDEX) + void defaultatoms_init(GlobalContext *glb); void platform_defaultatoms_init(GlobalContext *glb); diff --git a/src/libAtomVM/opcodes.h b/src/libAtomVM/opcodes.h index e06c7eb82d..63917229d9 100644 --- a/src/libAtomVM/opcodes.h +++ b/src/libAtomVM/opcodes.h @@ -121,7 +121,9 @@ #define OP_BS_CONTEXT_TO_BINARY 130 #define OP_BS_TEST_UNIT 131 #define OP_BS_MATCH_STRING 132 +#define OP_BS_INIT_WRITABLE 133 #define OP_BS_APPEND 134 +#define OP_BS_PRIVATE_APPEND 135 #define OP_TRIM 136 #define OP_BS_INIT_BITS 137 #define OP_BS_UTF8_SIZE 144 @@ -155,5 +157,8 @@ #define OP_RECV_MARKER_CLEAR 174 #define OP_RECV_MARKER_RESERVE 175 #define OP_RECV_MARKER_USE 176 +#define OP_BS_CREATE_BIN 177 +#define OP_CALL_FUN2 178 +#define OP_BADRECORD 180 #endif diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index 73ea3bc8f1..8a347e2926 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -43,6 +43,7 @@ #define ENABLE_OTP22 #define ENABLE_OTP23 #define ENABLE_OTP24 +#define ENABLE_OTP25 //#define ENABLE_TRACE @@ -780,6 +781,58 @@ typedef union RAISE_ERROR(BADARG_ATOM); \ } +#define CALL_FUN(fun, args_count, next_off) \ + Module *fun_module; \ + unsigned int fun_arity; \ + uint32_t n_freeze = 0; \ + uint32_t label; \ + const term *boxed_value = term_to_const_term_ptr(fun); \ + term index_or_function = boxed_value[2]; \ + if (term_is_atom(index_or_function)) { \ + term module = boxed_value[1]; \ + fun_arity = term_to_int(boxed_value[3]); \ + AtomString module_name = globalcontext_atomstring_from_term(mod->global, module); \ + AtomString function_name = globalcontext_atomstring_from_term(mod->global, index_or_function); \ + struct Nif *nif = (struct Nif *) nifs_get(module_name, function_name, fun_arity); \ + if (!IS_NULL_PTR(nif)) { \ + term return_value = nif->nif_ptr(ctx, arity, ctx->x); \ + if (UNLIKELY(term_is_invalid_term(return_value))) { \ + HANDLE_ERROR(); \ + } \ + ctx->x[0] = return_value; \ + NEXT_INSTRUCTION(next_off); \ + continue; \ + } else { \ + fun_module = globalcontext_get_module(ctx->global, module_name); \ + if (IS_NULL_PTR(fun_module)) { \ + HANDLE_ERROR(); \ + } \ + label = module_search_exported_function(fun_module, function_name, arity); \ + if (UNLIKELY(label == 0)) { \ + HANDLE_ERROR(); \ + } \ + } \ + } else { \ + fun_module = (Module *) boxed_value[1]; \ + uint32_t fun_index = term_to_int(index_or_function); \ + uint32_t fun_arity_and_freeze; \ + module_get_fun(fun_module, fun_index, &label, &fun_arity_and_freeze, &n_freeze); \ + fun_arity = fun_arity_and_freeze - n_freeze; \ + TRACE_CALL(ctx, mod, "call_fun", label, args_count); \ + } \ + if (UNLIKELY(args_count != fun_arity)) { \ + RAISE_ERROR(BADARITY_ATOM); \ + } \ + for (uint32_t i = 0; i < n_freeze; i++) { \ + ctx->x[i + fun_arity] = boxed_value[i + 3]; \ + } \ + NEXT_INSTRUCTION(next_off); \ + ctx->cp = module_address(mod->module_index, i); \ + mod = fun_module; \ + code = mod->code->code; \ + JUMP_TO_ADDRESS(mod->labels[label]); + + #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #ifdef IMPL_EXECUTE_LOOP @@ -3069,77 +3122,16 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f } term fun = ctx->x[args_count]; - if (UNLIKELY(!term_is_function(fun))) { + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } term new_error_tuple = term_alloc_tuple(2, ctx); - //TODO: ensure memory before term_put_tuple_element(new_error_tuple, 0, BADFUN_ATOM); term_put_tuple_element(new_error_tuple, 1, ctx->x[args_count]); - RAISE_ERROR(new_error_tuple); } - - Module *fun_module; - unsigned int fun_arity; - uint32_t n_freeze = 0; - uint32_t label; - - const term *boxed_value = term_to_const_term_ptr(fun); - term index_or_function = boxed_value[2]; - if (term_is_atom(index_or_function)) { - term module = boxed_value[1]; - fun_arity = term_to_int(boxed_value[3]); - - AtomString module_name = globalcontext_atomstring_from_term(mod->global, module); - AtomString function_name = globalcontext_atomstring_from_term(mod->global, index_or_function); - - struct Nif *nif = (struct Nif *) nifs_get(module_name, function_name, fun_arity); - if (!IS_NULL_PTR(nif)) { - term return_value = nif->nif_ptr(ctx, arity, ctx->x); - if (UNLIKELY(term_is_invalid_term(return_value))) { - HANDLE_ERROR(); - } - ctx->x[0] = return_value; - NEXT_INSTRUCTION(next_off); - continue; - - } else { - fun_module = globalcontext_get_module(ctx->global, module_name); - if (IS_NULL_PTR(fun_module)) { - HANDLE_ERROR(); - } - label = module_search_exported_function(fun_module, function_name, arity); - if (UNLIKELY(label == 0)) { - HANDLE_ERROR(); - } - } - - } else { - fun_module = (Module *) boxed_value[1]; - uint32_t fun_index = term_to_int(index_or_function); - - uint32_t fun_arity_and_freeze; - module_get_fun(fun_module, fun_index, &label, &fun_arity_and_freeze, &n_freeze); - - fun_arity = fun_arity_and_freeze - n_freeze; - - TRACE_CALL(ctx, mod, "call_fun", label, args_count); - } - - if (UNLIKELY(args_count != fun_arity)) { - RAISE_ERROR(BADARITY_ATOM); - } - - for (uint32_t i = 0; i < n_freeze; i++) { - ctx->x[i + fun_arity] = boxed_value[i + 3]; - } - - NEXT_INSTRUCTION(next_off); - ctx->cp = module_address(mod->module_index, i); - - mod = fun_module; - code = mod->code->code; - JUMP_TO_ADDRESS(mod->labels[label]); + CALL_FUN(fun, args_count, next_off) #endif #ifdef IMPL_CODE_LOADER @@ -3737,6 +3729,26 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f break; } + case OP_BS_INIT_WRITABLE: { + int next_off = 1; + + TRACE("bs_init_writable/0\n"); + + #ifdef IMPL_EXECUTE_LOOP + if (UNLIKELY(memory_ensure_free(ctx, term_binary_data_size_in_terms(0) + BINARY_HEADER_SIZE) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + term t = term_create_empty_binary(0, ctx); + + ctx->bs = t; + ctx->bs_offset = 0; + ctx->x[0] = t; + #endif + + NEXT_INSTRUCTION(next_off); + break; + } + case OP_BS_APPEND: { int next_off = 1; uint32_t fail; @@ -3764,7 +3776,7 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f DECODE_DEST_REGISTER(dreg, dreg_type, code, i, next_off); #ifdef IMPL_CODE_LOADER - TRACE("bs_append/6\n"); + TRACE("bs_append/8\n"); #endif #ifdef IMPL_EXECUTE_LOOP @@ -3783,7 +3795,7 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f RAISE_ERROR(UNSUPPORTED_ATOM); } - TRACE("bs_append/7, fail=%u size=%li unit=%u src=0x%lx dreg=%c%i\n", (unsigned) fail, size_val, (unsigned) unit, src, T_DEST_REG(dreg_type, dreg)); + TRACE("bs_append/8, fail=%u size=%li unit=%u src=0x%lx dreg=%c%i\n", (unsigned) fail, size_val, (unsigned) unit, src, T_DEST_REG(dreg_type, dreg)); size_t src_size = term_binary_size(src); // TODO: further investigate extra_val @@ -3804,6 +3816,61 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f break; } + case OP_BS_PRIVATE_APPEND: { + int next_off = 1; + uint32_t fail; + DECODE_LABEL(fail, code, i, next_off) + term size; + DECODE_COMPACT_TERM(size, code, i, next_off) + uint32_t unit; + DECODE_LITERAL(unit, code, i, next_off); + term src; + #ifdef IMPL_EXECUTE_LOOP + int src_off = next_off; + #endif + DECODE_COMPACT_TERM(src, code, i, next_off) + term flags; + UNUSED(flags); + DECODE_COMPACT_TERM(flags, code, i, next_off) + dreg_t dreg; + dreg_type_t dreg_type; + DECODE_DEST_REGISTER(dreg, dreg_type, code, i, next_off); + + #ifdef IMPL_CODE_LOADER + TRACE("bs_private_append/6\n"); + #endif + + #ifdef IMPL_EXECUTE_LOOP + VERIFY_IS_BINARY(src, "bs_private_append"); + VERIFY_IS_INTEGER(size, "bs_private_append"); + avm_int_t size_val = term_to_int(size); + + if (size_val % 8 != 0) { + TRACE("bs_private_append: size_val (%li) is not evenly divisible by 8\n", (long int) size_val, (long int) unit); + RAISE_ERROR(UNSUPPORTED_ATOM); + } + // TODO: further investigate unit. + // We currently do not support unaligned binaries, unit seems to be equal to 1 binary comprehensions + TRACE("bs_private_append/6, fail=%u size=%li unit=%u src=0x%lx dreg=%c%i\n", (unsigned) fail, size_val, (unsigned) unit, src, T_DEST_REG(dreg_type, dreg)); + + size_t src_size = term_binary_size(src); + if (UNLIKELY(memory_ensure_free(ctx, src_size + term_binary_data_size_in_terms(size_val / 8) + BINARY_HEADER_SIZE) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + DECODE_COMPACT_TERM(src, code, i, src_off) + term t = term_create_empty_binary(src_size + size_val / 8, ctx); + memcpy((void *) term_binary_data(t), (void *) term_binary_data(src), src_size); + + ctx->bs = t; + ctx->bs_offset = src_size * 8; + + WRITE_REGISTER(dreg_type, dreg, t); + #endif + + NEXT_INSTRUCTION(next_off); + break; + } + case OP_BS_PUT_INTEGER: { int next_off = 1; uint32_t fail; @@ -5836,6 +5903,345 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f } #endif +#ifdef ENABLE_OTP25 + case OP_BS_CREATE_BIN: { + int next_off = 1; + uint32_t fail; + DECODE_LABEL(fail, code, i, next_off); + uint32_t alloc; + DECODE_LITERAL(alloc, code, i, next_off); + uint32_t live; + DECODE_LITERAL(live, code, i, next_off); + uint32_t unit; + DECODE_LITERAL(unit, code, i, next_off); + dreg_t dreg; + dreg_type_t dreg_type; + DECODE_DEST_REGISTER(dreg, dreg_type, code, i, next_off); + TRACE("bs_create_bin/6 fail=%i, alloc=%i live=%i unit=%i dreg=%c%i\n", fail, alloc, live, unit, T_DEST_REG(dreg_type, dreg)); + DECODE_EXTENDED_LIST_TAG(code, i, next_off); + uint32_t list_len; + DECODE_LITERAL(list_len, code, i, next_off); + #ifdef IMPL_EXECUTE_LOOP + int list_off = next_off; + #endif + int nb_segments = list_len / 6; + #ifdef IMPL_CODE_LOADER + if (live > MAX_REG) { + fprintf(stderr, "Cannot use more than %d registers.\n", MAX_REG); + AVM_ABORT(); + } + if (list_len != nb_segments * 6) { + fprintf(stderr, "Unexpected number of operations for bs_create_bin/6, each segment should be 6 elements\n"); + AVM_ABORT(); + } + #endif + // Compute binary size in first iteration + #ifdef IMPL_EXECUTE_LOOP + size_t binary_size = 0; + #endif + for (int j = 0; j < nb_segments; j++) { + term atom_type; + DECODE_ATOM(atom_type, code, i, next_off); + int seg; + DECODE_LITERAL(seg, code, i, next_off); + int segment_unit; + DECODE_LITERAL(segment_unit, code, i, next_off); + term flags; + DECODE_COMPACT_TERM(flags, code, i, next_off); + term src; + DECODE_COMPACT_TERM(src, code, i, next_off); + term size; + DECODE_COMPACT_TERM(size, code, i, next_off); + #ifdef IMPL_EXECUTE_LOOP + avm_int_t src_value = 0; + switch (atom_type) { + case UTF8_ATOM: + case UTF16_ATOM: + case UTF32_ATOM: + VERIFY_IS_INTEGER(src, "bs_create_bin/6"); + src_value = term_to_int(src); + break; + } + size_t segment_size = 0; + switch (size) { + case UNDEFINED_ATOM: { + // Silently ignore segment_unit != 0 + segment_unit = 8; + switch (atom_type) { + case UTF8_ATOM: { + if (UNLIKELY(!bitstring_utf8_size(src_value, &segment_size))) { + RAISE_ERROR(BADARG_ATOM); + } + break; + } + case UTF16_ATOM: { + if (UNLIKELY(!bitstring_utf16_size(src_value, &segment_size))) { + RAISE_ERROR(BADARG_ATOM); + } + break; + } + case UTF32_ATOM: { + segment_size = 4; + break; + } + default: + // In Erlang/OTP #5281, this is a compile time check + fprintf(stderr, "Unexpected type %lx for bs_create_bin/6 size undefined\n", (long) atom_type); + AVM_ABORT(); + } + break; + } + case ALL_ATOM: { + if (atom_type != BINARY_ATOM && atom_type != APPEND_ATOM && atom_type != PRIVATE_APPEND_ATOM) { + // In Erlang/OTP #5281, this is a compile time check + fprintf(stderr, "Unexpected type for bs_create_bin/6 size all\n"); + AVM_ABORT(); + } + VERIFY_IS_BINARY(src, "bs_create_bin/6"); + // We only support src as a binary of bytes here. + segment_size = term_binary_size(src); + segment_unit = 8; + break; + } + default: { + if (UNLIKELY(!term_is_integer(size) || term_to_int(size) < 0)) { + RAISE_ERROR(BADARG_ATOM); + } + segment_size = term_to_int(size); + } + } + binary_size += segment_unit * segment_size; + #endif + } + // Allocate and build binary in second iteration + #ifdef IMPL_EXECUTE_LOOP + if (binary_size % 8) { + TRACE("bs_create_bin/6: total binary size (%li) is not evenly divisible by 8\n", binary_size); + RAISE_ERROR(UNSUPPORTED_ATOM); + } + context_clean_registers(ctx, live); + if (UNLIKELY(memory_ensure_free(ctx, alloc + term_binary_data_size_in_terms(binary_size / 8) + BINARY_HEADER_SIZE) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + term t = term_create_empty_binary(binary_size / 8, ctx); + size_t offset = 0; + + for (int j = 0; j < nb_segments; j++) { + term atom_type; + DECODE_ATOM(atom_type, code, i, list_off); + int seg; + DECODE_LITERAL(seg, code, i, list_off); + int segment_unit; + DECODE_LITERAL(segment_unit, code, i, list_off); + term flags; + DECODE_COMPACT_TERM(flags, code, i, list_off); + term src; + DECODE_COMPACT_TERM(src, code, i, list_off); + term size; + DECODE_COMPACT_TERM(size, code, i, list_off); + size_t segment_size; + avm_int_t flags_value = 0; + avm_int_t src_value = 0; + avm_int_t size_value = 0; + switch (atom_type) { + case UTF16_ATOM: + case UTF32_ATOM: + case INTEGER_ATOM: + while (term_is_nonempty_list(flags)) { + switch (term_get_list_head(flags)) { + case NATIVE_ATOM: + flags_value |= NativeEndianInteger; + break; + case LITTLE_ATOM: + flags_value |= LittleEndianInteger; + break; + default: + TRACE("bs_create_bin/6: Unknown flag atom %lx\n", (long) flags); + RAISE_ERROR(BADARG_ATOM); + } + flags = term_get_list_tail(flags); + } + if (UNLIKELY(!term_is_nil(flags))) { + TRACE("bs_create_bin/6: Flags not a proper list %lx\n", (long) flags); + RAISE_ERROR(BADARG_ATOM); + } + break; + default: + break; + } + switch (atom_type) { + case STRING_ATOM: + case UTF8_ATOM: + case UTF16_ATOM: + case UTF32_ATOM: + VERIFY_IS_INTEGER(src, "bs_create_bin/6"); + src_value = term_to_int(src); + break; + case INTEGER_ATOM: + VERIFY_IS_ANY_INTEGER(src, "bs_create_bin/6"); + src_value = term_maybe_unbox_int64(src); + break; + default: + break; + } + switch (atom_type) { + case INTEGER_ATOM: + case STRING_ATOM: + VERIFY_IS_INTEGER(size, "bs_create_bin/6"); + size_value = term_to_int(size); + break; + default: + break; + } + switch (atom_type) { + case UTF8_ATOM: { + bool result = bitstring_insert_utf8(t, offset, src_value, &segment_size); + if (UNLIKELY(!result)) { + TRACE("bs_create_bin/6: Failed to insert character as utf8 into binary: %i\n", result); + RAISE_ERROR(BADARG_ATOM); + } + segment_size *= 8; + break; + } + case UTF16_ATOM: { + bool result = bitstring_insert_utf16(t, offset, src_value, flags_value, &segment_size); + if (UNLIKELY(!result)) { + TRACE("bs_create_bin/6: Failed to insert character as utf16 into binary: %i\n", result); + RAISE_ERROR(BADARG_ATOM); + } + segment_size *= 8; + break; + } + case UTF32_ATOM: { + bool result = bitstring_insert_utf32(t, offset, src_value, flags_value); + if (UNLIKELY(!result)) { + TRACE("bs_create_bin/6: Failed to insert character as utf16 into binary: %i\n", result); + RAISE_ERROR(BADARG_ATOM); + } + segment_size = 32; + break; + } + case INTEGER_ATOM: { + bool result = bitstring_insert_integer(t, offset, src_value, size_value * segment_unit, flags_value); + if (UNLIKELY(!result)) { + TRACE("bs_create_bin/6: Failed to insert integer into binary\n"); + RAISE_ERROR(BADARG_ATOM); + } + segment_size = size_value; + break; + } + case STRING_ATOM: { + if (offset % 8) { + TRACE("bs_create_bin/6: current offset (%li) is not evenly divisible by 8\n", offset); + RAISE_ERROR(UNSUPPORTED_ATOM); + } + uint8_t *dst = (uint8_t *) term_binary_data(t) + (offset / 8); + size_t remaining = 0; + const uint8_t *str = module_get_str(mod, src_value, &remaining); + segment_size = size_value * segment_unit; + memcpy(dst, str, segment_size / 8); + break; + } + case APPEND_ATOM: + case BINARY_ATOM: + case PRIVATE_APPEND_ATOM: { + if (offset % 8) { + TRACE("bs_create_bin/6: current offset (%li) is not evenly divisible by 8\n", offset); + RAISE_ERROR(UNSUPPORTED_ATOM); + } + VERIFY_IS_BINARY(src, "bs_create_bin/6"); + uint8_t *dst = (uint8_t *) term_binary_data(t) + (offset / 8); + const uint8_t *bin = (const uint8_t *) term_binary_data(src); + size_t binary_size = term_binary_size(src); + if (size != ALL_ATOM) { + VERIFY_IS_INTEGER(size, "bs_create_bin/6"); + size_value = term_to_int(size); + if (size_value > binary_size) { + RAISE_ERROR(BADARG_ATOM); + } + binary_size = size_value; + } + memcpy(dst, bin, binary_size); + segment_size = binary_size * 8; + break; + } + default: { + TRACE("bs_create_bin/6: unsupported type atom_index=%i\n", (int) term_to_atom_index(atom_type)); + RAISE_ERROR(UNSUPPORTED_ATOM); + } + } + offset += segment_size; + } + WRITE_REGISTER(dreg_type, dreg, t); + #endif + + NEXT_INSTRUCTION(next_off); + break; + } + + case OP_CALL_FUN2: { + int next_off = 1; + term tag; + DECODE_COMPACT_TERM(tag, code, i, next_off) + unsigned int args_count; + DECODE_LITERAL(args_count, code, i, next_off) + #ifdef IMPL_EXECUTE_LOOP + int fun_off = next_off; + #endif + term fun; + DECODE_COMPACT_TERM(fun, code, i, next_off) + + TRACE("call_fun2/3, tag, args_count=%i, fun\n", args_count); + USED_BY_TRACE(args_count); + + #ifdef IMPL_EXECUTE_LOOP + if (UNLIKELY(!term_is_function(fun))) { + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + // Decode the function again after GC was possibly run + DECODE_COMPACT_TERM(fun, code, i, fun_off) + term new_error_tuple = term_alloc_tuple(2, ctx); + term_put_tuple_element(new_error_tuple, 0, BADFUN_ATOM); + term_put_tuple_element(new_error_tuple, 1, fun); + RAISE_ERROR(new_error_tuple); + } + CALL_FUN(fun, args_count, next_off) + #endif + + #ifdef IMPL_CODE_LOADER + NEXT_INSTRUCTION(next_off); + #endif + + break; + } + + case OP_BADRECORD: { + int next_off = 1; + TRACE("badrecord/1\n"); + + #ifdef IMPL_EXECUTE_LOOP + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + term value; + DECODE_COMPACT_TERM(value, code, i, next_off) + term new_error_tuple = term_alloc_tuple(2, ctx); + term_put_tuple_element(new_error_tuple, 0, BADRECORD_ATOM); + term_put_tuple_element(new_error_tuple, 1, value); + RAISE_ERROR(new_error_tuple); + #endif + + #ifdef IMPL_CODE_LOADER + term value; + DECODE_COMPACT_TERM(value, code, i, next_off) + NEXT_INSTRUCTION(next_off); + #endif + + break; + } +#endif + default: printf("Undecoded opcode: %i\n", code[i]); #ifdef IMPL_EXECUTE_LOOP diff --git a/src/platforms/esp32/components/avm_sys/include/platform_defaultatoms.h b/src/platforms/esp32/components/avm_sys/include/platform_defaultatoms.h index 5b4f296a12..db896ab469 100644 --- a/src/platforms/esp32/components/avm_sys/include/platform_defaultatoms.h +++ b/src/platforms/esp32/components/avm_sys/include/platform_defaultatoms.h @@ -46,26 +46,25 @@ #define ADDRESS_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 9) #define PORT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 10) #define CONTROLLING_PROCESS_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 11) -#define BINARY_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 12) -#define ACTIVE_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 13) -#define BUFFER_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 14) -#define CONNECT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 15) -#define SEND_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 16) -#define TCP_CLOSED_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 17) -#define RECV_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 18) -#define LISTEN_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 19) -#define BACKLOG_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 20) -#define ACCEPT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 21) -#define FD_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 22) - -#define INIT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 23) -#define CLOSE_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 24) -#define GET_PORT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 25) -#define SOCKNAME_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 26) -#define PEERNAME_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 27) -#define NOT_OWNER_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 28) - -#define NETWORK_ATOMS_BASE_INDEX (SOCKET_ATOMS_BASE_INDEX + 29) +#define ACTIVE_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 12) +#define BUFFER_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 13) +#define CONNECT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 14) +#define SEND_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 15) +#define TCP_CLOSED_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 16) +#define RECV_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 17) +#define LISTEN_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 18) +#define BACKLOG_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 19) +#define ACCEPT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 20) +#define FD_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 21) + +#define INIT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 22) +#define CLOSE_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 23) +#define GET_PORT_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 24) +#define SOCKNAME_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 25) +#define PEERNAME_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 26) +#define NOT_OWNER_ATOM_INDEX (SOCKET_ATOMS_BASE_INDEX + 27) + +#define NETWORK_ATOMS_BASE_INDEX (SOCKET_ATOMS_BASE_INDEX + 28) #define STA_ATOM_INDEX (NETWORK_ATOMS_BASE_INDEX + 0) #define SSID_ATOM_INDEX (NETWORK_ATOMS_BASE_INDEX + 1) #define PSK_ATOM_INDEX (NETWORK_ATOMS_BASE_INDEX + 2) @@ -136,7 +135,6 @@ #define ADDRESS_ATOM term_from_atom_index(ADDRESS_ATOM_INDEX) #define PORT_ATOM term_from_atom_index(PORT_ATOM_INDEX) #define CONTROLLING_PROCESS_ATOM TERM_FROM_ATOM_INDEX(CONTROLLING_PROCESS_ATOM_INDEX) -#define BINARY_ATOM term_from_atom_index(BINARY_ATOM_INDEX) #define ACTIVE_ATOM term_from_atom_index(ACTIVE_ATOM_INDEX) #define BUFFER_ATOM term_from_atom_index(BUFFER_ATOM_INDEX) #define CONNECT_ATOM term_from_atom_index(CONNECT_ATOM_INDEX) diff --git a/src/platforms/esp32/components/avm_sys/platform_defaultatoms.c b/src/platforms/esp32/components/avm_sys/platform_defaultatoms.c index a72ae1bee3..6e45a77555 100644 --- a/src/platforms/esp32/components/avm_sys/platform_defaultatoms.c +++ b/src/platforms/esp32/components/avm_sys/platform_defaultatoms.c @@ -42,7 +42,6 @@ static const char *const sendto_atom = "\x6" "sendto"; static const char *const address_atom = "\x7" "address"; static const char *const port_atom = "\x4" "port"; static const char *const controlling_process_atom = "\x13" "controlling_process"; -static const char *const binary_atom = "\x6" "binary"; static const char *const active_atom = "\x6" "active"; static const char *const buffer_atom = "\x6" "buffer"; static const char *const connect_atom = "\x7" "connect"; @@ -134,7 +133,6 @@ void platform_defaultatoms_init(GlobalContext *glb) ok &= globalcontext_insert_atom(glb, address_atom) == ADDRESS_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, port_atom) == PORT_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, controlling_process_atom) == CONTROLLING_PROCESS_ATOM_INDEX; - ok &= globalcontext_insert_atom(glb, binary_atom) == BINARY_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, active_atom) == ACTIVE_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, buffer_atom) == BUFFER_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, connect_atom) == CONNECT_ATOM_INDEX; diff --git a/src/platforms/generic_unix/lib/platform_defaultatoms.c b/src/platforms/generic_unix/lib/platform_defaultatoms.c index e2f8c9c78a..8d793a4063 100644 --- a/src/platforms/generic_unix/lib/platform_defaultatoms.c +++ b/src/platforms/generic_unix/lib/platform_defaultatoms.c @@ -38,7 +38,6 @@ static const char *const sta_connected_atom = "\xD" "sta_connected"; static const char *const address_atom = "\x7" "address"; static const char *const port_atom = "\x4" "port"; static const char *const controlling_process_atom = "\x13" "controlling_process"; -static const char *const binary_atom = "\x6" "binary"; static const char *const active_atom = "\x6" "active"; static const char *const buffer_atom = "\x6" "buffer"; @@ -75,7 +74,6 @@ void platform_defaultatoms_init(GlobalContext *glb) ok &= globalcontext_insert_atom(glb, address_atom) == ADDRESS_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, port_atom) == PORT_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, controlling_process_atom) == CONTROLLING_PROCESS_ATOM_INDEX; - ok &= globalcontext_insert_atom(glb, binary_atom) == BINARY_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, active_atom) == ACTIVE_ATOM_INDEX; ok &= globalcontext_insert_atom(glb, buffer_atom) == BUFFER_ATOM_INDEX; diff --git a/src/platforms/generic_unix/lib/platform_defaultatoms.h b/src/platforms/generic_unix/lib/platform_defaultatoms.h index 815905efbf..b9923a6d95 100644 --- a/src/platforms/generic_unix/lib/platform_defaultatoms.h +++ b/src/platforms/generic_unix/lib/platform_defaultatoms.h @@ -41,20 +41,19 @@ #define ADDRESS_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 13) #define PORT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 14) #define CONTROLLING_PROCESS_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 15) -#define BINARY_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 16) -#define ACTIVE_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 17) -#define BUFFER_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 18) +#define ACTIVE_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 16) +#define BUFFER_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 17) -#define GETADDRINFO_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 19) -#define NO_SUCH_HOST_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 20) -#define CONNECT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 21) -#define TCP_CLOSED_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 22) +#define GETADDRINFO_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 18) +#define NO_SUCH_HOST_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 19) +#define CONNECT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 20) +#define TCP_CLOSED_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 21) -#define LISTEN_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 23) -#define BACKLOG_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 24) -#define ACCEPT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 25) -#define FD_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 26) -#define GENERIC_UNIX_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 27) +#define LISTEN_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 22) +#define BACKLOG_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 23) +#define ACCEPT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 24) +#define FD_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 25) +#define GENERIC_UNIX_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 26) #define PROTO_ATOM term_from_atom_index(PROTO_ATOM_INDEX) #define UDP_ATOM term_from_atom_index(UDP_ATOM_INDEX) @@ -74,7 +73,6 @@ #define ADDRESS_ATOM term_from_atom_index(ADDRESS_ATOM_INDEX) #define PORT_ATOM term_from_atom_index(PORT_ATOM_INDEX) #define CONTROLLING_PROCESS_ATOM term_from_atom_index(CONTROLLING_PROCESS_ATOM_INDEX) -#define BINARY_ATOM term_from_atom_index(BINARY_ATOM_INDEX) #define ACTIVE_ATOM term_from_atom_index(ACTIVE_ATOM_INDEX) #define BUFFER_ATOM term_from_atom_index(BUFFER_ATOM_INDEX) diff --git a/tests/erlang_tests/test_bs.erl b/tests/erlang_tests/test_bs.erl index b51ae5197d..9ff7c117d5 100644 --- a/tests/erlang_tests/test_bs.erl +++ b/tests/erlang_tests/test_bs.erl @@ -61,6 +61,8 @@ start() -> <<1, 2, 3, 1, 2, 3, 4, 5, 6>> = test_bs_append(<<1, 2, 3>>, <<1, 2, 3, 4, 5, 6>>), + <<1, 2, 3>> = test_bs_private_append(<<1, 2, 3>>), + nope = test_match_clause(<<"">>), nope = test_match_clause(<<16#FF>>), nope = test_match_clause(<<$n:8>>), @@ -265,6 +267,9 @@ test_match_first_integer(_) -> test_bs_append(Bin1, Bin2) -> <>. +test_bs_private_append(Bin) -> + <<<> || <> <= Bin>>. + test_match_clause( <<$n:8, FixedBinaryData:4/binary, Rest/binary>> ) ->