Skip to content

Commit 67b2dae

Browse files
bjorngjhogberglucioleKiilya-klyuchnikov
committed
Implement native records
This is an implementation of native records as desribed in EEP-79 (erlang/eep#81). Co-authored-by: John Högberg <[email protected]> Co-authored-by: Isabell Huang <[email protected]> Co-authored-by: Ilya Klyuchnikov <[email protected]>
1 parent 901053a commit 67b2dae

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+5088
-403
lines changed

erts/emulator/Makefile.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ JIT_OBJS += \
10741074
$(OBJDIR)/instr_guard_bifs.o \
10751075
$(OBJDIR)/instr_map.o \
10761076
$(OBJDIR)/instr_msg.o \
1077+
$(OBJDIR)/instr_records.o \
10771078
$(OBJDIR)/instr_select.o \
10781079
$(OBJDIR)/instr_trace.o
10791080
else
@@ -1088,6 +1089,7 @@ JIT_OBJS += \
10881089
$(OBJDIR)/instr_guard_bifs.o \
10891090
$(OBJDIR)/instr_map.o \
10901091
$(OBJDIR)/instr_msg.o \
1092+
$(OBJDIR)/instr_records.o \
10911093
$(OBJDIR)/instr_select.o \
10921094
$(OBJDIR)/instr_trace.o
10931095
endif
@@ -1161,7 +1163,8 @@ RUN_OBJS += \
11611163
$(OBJDIR)/erl_term_hashing.o \
11621164
$(OBJDIR)/erl_bif_coverage.o \
11631165
$(OBJDIR)/erl_iolist.o \
1164-
$(OBJDIR)/erl_etp.o
1166+
$(OBJDIR)/erl_etp.o \
1167+
$(OBJDIR)/erl_struct.o
11651168

11661169
LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
11671170

erts/emulator/beam/atom.names

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ atom await_sched_wall_time_modifications
124124
atom awaiting_load
125125
atom awaiting_unload
126126
atom backtrace backtrace_depth
127-
atom badarg badarith badarity badfile badfun badkey badmap badmatch
127+
atom badarg badarith badarity badfield badfile badfun badkey badmap badmatch
128128
atom badrecord badsig
129129
atom badopt badtype
130130
atom bad_map_iterator
@@ -533,6 +533,7 @@ atom notempty_atstart
533533
atom notify
534534
atom notsup
535535
atom nouse_stdio
536+
atom novalue
536537
atom nul
537538
atom off_heap
538539
atom offset

erts/emulator/beam/beam_common.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,9 @@ Eterm error_atom[NUMBER_EXIT_CODES] = {
400400
am_notsup, /* 17 */
401401
am_badmap, /* 18 */
402402
am_badkey, /* 19 */
403-
am_badrecord, /* 20 */
403+
am_badrecord, /* 20 */
404+
am_badfield, /* 21 */
405+
am_novalue, /* 22 */
404406
};
405407

406408
/* Returns the return address at E[0] in printable form, skipping tracing in
@@ -757,6 +759,8 @@ expand_error_value(Process* c_p, Uint freason, Eterm Value) {
757759
case (GET_EXC_INDEX(EXC_BADMAP)):
758760
case (GET_EXC_INDEX(EXC_BADKEY)):
759761
case (GET_EXC_INDEX(EXC_BADRECORD)):
762+
case (GET_EXC_INDEX(EXC_BADFIELD)):
763+
case (GET_EXC_INDEX(EXC_NOVALUE)):
760764
/* Some common exceptions: value -> {atom, value} */
761765
ASSERT(is_value(Value));
762766
hp = HAlloc(c_p, 3);

erts/emulator/beam/beam_common.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ do { \
126126
dst = x(loader_x_reg_index(dst)); \
127127
break; \
128128
case LOADER_Y_REG: \
129-
ASSERT(loader_y_reg_index(dst) >= 1); \
130129
dst = y(loader_y_reg_index(dst)); \
131130
break; \
132131
} \

erts/emulator/beam/beam_debug.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,10 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
964964
case op_update_map_assoc_cdtI:
965965
case op_update_map_exact_xjdtI:
966966
case op_update_map_exact_yjdtI:
967+
case op_i_create_native_record_tcdtI:
968+
case op_i_update_native_record_acsdtI:
969+
case op_i_get_record_elements_fxI:
970+
case op_i_get_record_elements_fyI:
967971
{
968972
int n = unpacked[-1];
969973

erts/emulator/beam/beam_file.c

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "erl_unicode.h"
3232
#include "erl_binary.h"
3333
#include "erl_global_literals.h"
34+
#include "erl_struct.h"
3435

3536
#define LoadError(Expr) \
3637
do { \
@@ -874,6 +875,247 @@ static int parse_debug_chunk(BeamFile *beam, IFF_Chunk *chunk) {
874875
}
875876
}
876877

878+
struct erl_record_field {
879+
int order;
880+
Eterm key;
881+
Eterm value;
882+
};
883+
884+
static int record_compare(const struct erl_record_field *a, const struct erl_record_field *b) {
885+
Sint res = erts_cmp_flatmap_keys(a->key, b->key);
886+
887+
if (res < 0) {
888+
return -1;
889+
} else if (res > 0) {
890+
return 1;
891+
}
892+
893+
return 0;
894+
}
895+
896+
static int parse_record_chunk_data(BeamFile *beam, BeamReader *p_reader) {
897+
Sint32 record_count;
898+
Sint32 total_field_count;
899+
BeamOpAllocator op_allocator;
900+
BeamCodeReader *op_reader;
901+
BeamOp *op = NULL;
902+
BeamFile_RecordTable *rec = &beam->record;
903+
struct erl_record_field *fields = NULL;
904+
905+
LoadAssert(beamreader_read_i32(p_reader, &record_count));
906+
LoadAssert(beamreader_read_i32(p_reader, &total_field_count));
907+
908+
beamopallocator_init(&op_allocator);
909+
910+
op_reader = erts_alloc(ERTS_ALC_T_PREPARED_CODE, sizeof(BeamCodeReader));
911+
912+
op_reader->allocator = &op_allocator;
913+
op_reader->file = beam;
914+
op_reader->pending = NULL;
915+
op_reader->first = 1;
916+
op_reader->reader = *p_reader;
917+
918+
if (record_count < 0) {
919+
goto error;
920+
}
921+
922+
rec->record_count = record_count;
923+
rec->total_field_count = 2 * total_field_count;
924+
rec->records = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
925+
record_count * sizeof(BeamFile_Record));
926+
927+
for (Sint32 i = 0; i < record_count; i++) {
928+
BeamOpArg *arg;
929+
int extra_args;
930+
int field_index;
931+
Uint tmp_size;
932+
Uint struct_def_size;
933+
Uint num_fields;
934+
Eterm *order_tuple;
935+
ErtsStructDefinition *tmp_def;
936+
Eterm is_exported;
937+
938+
if (!beamcodereader_next(op_reader, &op)) {
939+
goto error;
940+
}
941+
if (op->op != genop_call_last_3) {
942+
goto error;
943+
}
944+
945+
arg = op->a;
946+
947+
/* Process name. */
948+
switch (arg->type) {
949+
case TAG_a:
950+
if (is_atom(arg->val)) {
951+
rec->records[i].name = arg->val;
952+
} else {
953+
goto error;
954+
}
955+
break;
956+
default:
957+
goto error;
958+
}
959+
960+
arg++;
961+
962+
/* Process exported flag. */
963+
switch (arg->type) {
964+
case TAG_a:
965+
if (arg->val == am_true || arg->val == am_false) {
966+
is_exported = arg->val;
967+
} else {
968+
goto error;
969+
}
970+
break;
971+
default:
972+
goto error;
973+
}
974+
975+
arg++;
976+
977+
/* Get and check the number of extra arguments. */
978+
if (arg->type != TAG_u) {
979+
goto error;
980+
}
981+
982+
extra_args = arg->val;
983+
984+
arg++;
985+
986+
if (extra_args % 2 != 0) {
987+
goto error;
988+
}
989+
990+
rec->records[i].num_fields = num_fields = extra_args / 2;
991+
992+
/* Collect field names and default values. Put it into an
993+
* array of erl_record_fields structs, which are suitable for
994+
* sorting. */
995+
fields = erts_alloc(ERTS_ALC_T_TMP, num_fields * sizeof(struct erl_record_field));
996+
field_index = 0;
997+
while (extra_args > 0) {
998+
fields[field_index].order = field_index;
999+
1000+
switch (arg[0].type) {
1001+
case TAG_a:
1002+
fields[field_index].key = arg[0].val;
1003+
break;
1004+
default:
1005+
goto error;
1006+
}
1007+
1008+
switch (arg[1].type) {
1009+
case TAG_u:
1010+
fields[field_index].value = make_catch(0);
1011+
break;
1012+
case TAG_a:
1013+
case TAG_n:
1014+
fields[field_index].value = arg[1].val;
1015+
break;
1016+
case TAG_i:
1017+
fields[field_index].value = make_small(arg[1].val);
1018+
break;
1019+
case TAG_q:
1020+
fields[field_index].value = beamfile_get_literal(beam, arg[1].val);
1021+
break;
1022+
default:
1023+
goto error;
1024+
}
1025+
1026+
field_index++;
1027+
arg += 2;
1028+
extra_args -= 2;
1029+
}
1030+
1031+
qsort((void *) fields, num_fields, sizeof(struct erl_record_field),
1032+
(int (*)(const void *, const void *)) record_compare);
1033+
1034+
/* Separate the fields into an array of field names and default values,
1035+
* and a tuple mapping from original position to current position. */
1036+
1037+
tmp_size = sizeof(ErtsStructDefinition);
1038+
tmp_size += 2 * num_fields * sizeof(Eterm); /* Fields & default values */
1039+
1040+
struct_def_size = tmp_size;
1041+
tmp_size += (num_fields + 1) * sizeof(Eterm); /* Order tuple */
1042+
1043+
tmp_def = (ErtsStructDefinition *) erts_alloc(ERTS_ALC_T_TMP, tmp_size);
1044+
tmp_def->thing_word = make_arityval(struct_def_size/sizeof(Eterm) - 1);
1045+
tmp_def->entry = NIL;
1046+
tmp_def->module = beam->module;
1047+
tmp_def->name = rec->records[i].name;
1048+
tmp_def->is_exported = is_exported;
1049+
1050+
order_tuple = &tmp_def->fields[num_fields].key;
1051+
1052+
if (rec->records[i].num_fields == 0) {
1053+
*order_tuple = NIL;
1054+
tmp_def->field_order = ERTS_GLOBAL_LIT_EMPTY_TUPLE;
1055+
} else {
1056+
tmp_def->field_order = make_tuple(order_tuple);
1057+
*order_tuple++ = make_arityval(num_fields);
1058+
}
1059+
1060+
for (field_index = 0; field_index < num_fields; field_index++) {
1061+
tmp_def->fields[field_index].key = fields[field_index].key;
1062+
tmp_def->fields[field_index].value = fields[field_index].value;
1063+
order_tuple[fields[field_index].order] = make_small(field_index);
1064+
}
1065+
1066+
/* Save everything into a literal. */
1067+
rec->records[i].def_literal =
1068+
beamfile_add_literal(beam, make_tuple((Eterm *)tmp_def), 0);
1069+
1070+
erts_free(ERTS_ALC_T_TMP, tmp_def);
1071+
1072+
erts_free(ERTS_ALC_T_TMP, fields);
1073+
fields = NULL;
1074+
1075+
beamopallocator_free_op(&op_allocator, op);
1076+
op = NULL;
1077+
}
1078+
1079+
beamcodereader_close(op_reader);
1080+
beamopallocator_dtor(&op_allocator);
1081+
1082+
return 1;
1083+
1084+
error:
1085+
if (op != NULL) {
1086+
beamopallocator_free_op(&op_allocator, op);
1087+
}
1088+
1089+
if (fields != NULL) {
1090+
erts_free(ERTS_ALC_T_TMP, fields);
1091+
}
1092+
1093+
beamcodereader_close(op_reader);
1094+
beamopallocator_dtor(&op_allocator);
1095+
1096+
if (rec->records) {
1097+
erts_free(ERTS_ALC_T_PREPARED_CODE, rec->records);
1098+
rec->records = NULL;
1099+
}
1100+
1101+
return 0;
1102+
}
1103+
1104+
static int parse_record_chunk(BeamFile *beam, IFF_Chunk *chunk) {
1105+
BeamReader reader;
1106+
Sint32 version;
1107+
1108+
beamreader_init(chunk->data, chunk->size, &reader);
1109+
1110+
LoadAssert(beamreader_read_i32(&reader, &version));
1111+
1112+
if (version == 0) {
1113+
return parse_record_chunk_data(beam, &reader);
1114+
} else {
1115+
return 0;
1116+
}
1117+
}
1118+
8771119
static ErlHeapFragment *new_literal_fragment(Uint size)
8781120
{
8791121
ErlHeapFragment *bp;
@@ -1128,6 +1370,7 @@ beamfile_read(const byte *data, size_t size, BeamFile *beam) {
11281370
MakeIffId('T', 'y', 'p', 'e'), /* 12 */
11291371
MakeIffId('M', 'e', 't', 'a'), /* 13 */
11301372
MakeIffId('D', 'b', 'g', 'B'), /* 14 */
1373+
MakeIffId('R', 'e', 'c', 's'), /* 15 */
11311374
};
11321375

11331376
static const int UTF8_ATOM_CHUNK = 0;
@@ -1147,6 +1390,7 @@ beamfile_read(const byte *data, size_t size, BeamFile *beam) {
11471390
static const int TYPE_CHUNK = 12;
11481391
static const int META_CHUNK = 13;
11491392
static const int DEBUG_CHUNK = 14;
1393+
static const int RECORD_CHUNK = 15;
11501394

11511395
static const int NUM_CHUNKS = sizeof(chunk_iffs) / sizeof(chunk_iffs[0]);
11521396

@@ -1251,6 +1495,13 @@ beamfile_read(const byte *data, size_t size, BeamFile *beam) {
12511495
}
12521496
}
12531497

1498+
if (chunks[RECORD_CHUNK].size > 0) {
1499+
if (!parse_record_chunk(beam, &chunks[RECORD_CHUNK])) {
1500+
error = BEAMFILE_READ_CORRUPT_RECORD_TABLE;
1501+
goto error;
1502+
}
1503+
}
1504+
12541505
beam->strings.data = chunks[STR_CHUNK].data;
12551506
beam->strings.size = chunks[STR_CHUNK].size;
12561507

@@ -1326,6 +1577,12 @@ beamfile_read(const byte *data, size_t size, BeamFile *beam) {
13261577
chunks[META_CHUNK].size);
13271578
}
13281579

1580+
if (chunks[RECORD_CHUNK].size > 0) {
1581+
MD5Update(&md5,
1582+
(byte*)chunks[RECORD_CHUNK].data,
1583+
chunks[RECORD_CHUNK].size);
1584+
}
1585+
13291586
MD5Final(beam->checksum, &md5);
13301587
}
13311588

0 commit comments

Comments
 (0)