// is undefined behaviour if it is called more than once.
__initialize_data_segments :: () -> void ---
+// This is also a special compiler generated procedure that calls all procedures specified with
+// #init, in the specified order. It should theoritically only be called once on the main thread.
+__run_init_procedures :: () -> void ---
__exit(1);
}
+// This procedure should only be called once at the very begining
+// of the program. It initializes the data segments, the global heap
+// allocator, as well as thread-local storage, the context, and stdio
+// on the main thread. Finally it runs initialization procedures, which
+// should only happen once.
__runtime_initialize :: () {
__initialize_data_segments();
__thread_initialize();
#if Multi_Threading_Enabled do thread.__initialize();
+
+ __run_init_procedures();
}
// Use this procedure to initialize everything needed in the
- Useful for map.get_ptr (^arr[10] or ^people["John"])
[ ] Complete the set of combinations of procedure declaration syntaxes
- Currently (x: i32) => { ... } isn't legal.
+ [X] #init functions (run before main, added to list at compile time, emitted with __run_init_procedures())
[ ] Tests for new language features
- Do blocks
- Code blocks
NODE(DirectiveExport) \
NODE(DirectiveDefined) \
NODE(DirectiveTag) \
+ NODE(DirectiveInit) \
\
NODE(Return) \
NODE(Jump) \
Ast_Kind_Directive_Export,
Ast_Kind_Directive_Defined,
Ast_Kind_Directive_Tag,
+ Ast_Kind_Directive_Init,
Ast_Kind_Call_Site,
Ast_Kind_Code_Block,
AstTyped *code_expr;
};
+struct AstDirectiveInit {
+ AstNode_base;
+
+ AstTyped *init_proc;
+ bh_arr(AstDirectiveInit *) dependencies;
+};
+
struct AstMacro {
AstTyped_base;
extern AstType *builtin_code_type;
extern AstTyped *type_table_node;
extern AstFunction *builtin_initialize_data_segments;
+extern AstFunction *builtin_run_init_procedures;
+extern bh_arr(AstFunction *) init_procedures;
typedef struct BuiltinSymbol {
char* package;
"EXPORT",
"DEFINED",
"TAG",
+ "INIT",
"CALL SITE",
"CODE BLOCK",
AstType *builtin_any_type;
AstType *builtin_code_type;
-AstFunction *builtin_initialize_data_segments = NULL;
AstTyped *type_table_node = NULL;
+AstFunction *builtin_initialize_data_segments = NULL;
+AstFunction *builtin_run_init_procedures = NULL;
+bh_arr(AstFunction *) init_procedures = NULL;
const BuiltinSymbol builtin_symbols[] = {
{ NULL, "void", (AstNode *) &basic_type_void },
return;
}
+ builtin_run_init_procedures = (AstFunction *) symbol_raw_resolve(p->scope, "__run_init_procedures");
+ if (builtin_run_init_procedures == NULL || builtin_run_init_procedures->kind != Ast_Kind_Function) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'__run_init_procedures");
+ return;
+ }
+
+ bh_arr_new(global_heap_allocator, init_procedures, 4);
+
p = package_lookup("builtin.type_info");
if (p != NULL) {
type_table_node = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
return Check_Success;
}
+ if (expr->kind == Ast_Kind_Directive_Init) {
+ ERROR(expr->token->pos, "#init declarations are not in normal expressions, only in #after clauses.");
+ }
+
fill_in_type(expr);
current_checking_level = EXPRESSION_LEVEL;
}
}
+ if (directive->kind == Ast_Kind_Directive_Init) {
+ AstDirectiveInit *init = (AstDirectiveInit *) directive;
+ if ((init->flags & Ast_Flag_Has_Been_Checked) == 0) {
+ CHECK(expression, &init->init_proc);
+
+ if (init->init_proc->kind != Ast_Kind_Function) {
+ ERROR_(init->token->pos, "#init only works for functions, got '%s'", onyx_ast_node_kind_string(init->init_proc->kind));
+ }
+
+ assert(init->init_proc->type);
+ if (init->init_proc->type->Function.param_count != 0) {
+ ERROR(init->token->pos, "#init expects a function that takes 0 arguments.");
+ }
+ }
+
+ init->flags |= Ast_Flag_Has_Been_Checked;
+
+ if (init->dependencies) {
+ i32 i = 0;
+ bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
+ AstTyped *d = (AstTyped *) strip_aliases((AstNode *) *dependency);
+ if (d->kind != Ast_Kind_Directive_Init) {
+ ERROR_(init->token->pos, "All dependencies of an #init must be another #init. The %d%s dependency was not.", i + 1, bh_num_suffix(i + 1));
+ }
+
+ assert(d->entity);
+ if (d->entity->state != Entity_State_Finalized) {
+ YIELD(init->token->pos, "Circular dependency in #init nodes. Here are the nodes involved.");
+ }
+
+ i++;
+ }
+ }
+
+ bh_arr_push(init_procedures, (AstFunction *) init->init_proc);
+ return Check_Complete;
+ }
+
return Check_Success;
}
case Ast_Kind_Code_Block:
case Ast_Kind_Macro:
case Ast_Kind_File_Contents:
+ case Ast_Kind_Symbol:
return 0;
default: return 1;
case Ast_Kind_Directive_Export:
case Ast_Kind_Directive_Add_Overload:
case Ast_Kind_Directive_Tag:
- case Ast_Kind_Directive_Operator: {
+ case Ast_Kind_Directive_Operator:
+ case Ast_Kind_Directive_Init: {
ent.type = Entity_Type_Process_Directive;
ent.expr = (AstTyped *) node;
ENTITY_INSERT(ent);
return NULL;
}
+static AstDirectiveInit* parse_init_directive(OnyxParser *parser, OnyxToken *token) {
+ AstDirectiveInit *init = make_node(AstDirectiveInit, Ast_Kind_Directive_Init);
+ init->token = token;
+
+ parser->parse_calls = 0;
+ while (parse_possible_directive(parser, "after")) {
+ if (parser->hit_unexpected_token) return init;
+ if (init->dependencies == NULL) bh_arr_new(global_heap_allocator, init->dependencies, 2);
+
+ AstTyped *dependency = parse_expression(parser, 0);
+ bh_arr_push(init->dependencies, (AstDirectiveInit *) dependency);
+ }
+ parser->parse_calls = 1;
+
+ init->init_proc = parse_expression(parser, 0);
+ ENTITY_SUBMIT(init);
+ return init;
+}
+
static AstTyped* parse_top_level_expression(OnyxParser* parser) {
if (parser->curr->type == Token_Type_Keyword_Global) return parse_global_declaration(parser);
if (parser->curr->type == Token_Type_Keyword_Struct) return (AstTyped *) parse_struct(parser);
return (AstTyped *) ofunc;
}
+ if (parse_possible_directive(parser, "init")) {
+ // :LinearTokenDependent
+ AstDirectiveInit *init = parse_init_directive(parser, parser->curr - 2);
+ return (AstTyped *) init;
+ }
+
return parse_expression(parser, 1);
}
break;
}
+ case Ast_Kind_Directive_Init: break;
+
case Ast_Kind_Global: ((AstGlobal *) node)->name = generate_name_within_scope(parser, symbol);
case Ast_Kind_Overloaded_Function:
goto submit_binding_to_entities;
}
+ else if (parse_possible_directive(parser, "init")) {
+ // :LinearTokenDependent
+ parse_init_directive(parser, parser->curr - 2);
+ return;
+ }
else {
OnyxToken* directive_token = expect_token(parser, '#');
OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
SYMRES(expression, &tag->expr);
break;
}
+
+ case Ast_Kind_Directive_Init: {
+ AstDirectiveInit *init = (AstDirectiveInit *) directive;
+ SYMRES(expression, &init->init_proc);
+
+ if (init->dependencies) {
+ bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
+ SYMRES(expression, (AstTyped **) dependency);
+ }
+ }
+
+ break;
+ }
}
return Symres_Success;
return;
}
+ if (fd == builtin_run_init_procedures) {
+ emit_run_init_procedures(mod, &wasm_func.code);
+ bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
+ bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+ return;
+ }
+
if (fd->body != NULL) {
// NOTE: Generate the local map
u64 localidx = 0;
}
*pcode = code;
-}
\ No newline at end of file
+}
+
+EMIT_FUNC_NO_ARGS(run_init_procedures) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ bh_arr_each(AstFunction *, func, init_procedures) {
+ i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) *func);
+ bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
+ }
+
+ *pcode = code;
+}