| author | |
| committer | |
| log | c9ae30d27e123f87fc9c10b6af2e380eab57d6df |
| tree | 97c8144f1f77a37f12ad8582cd56778d596d3181 |
| parent | c400cb429abf2980962739731f5b64861a54ab61 |
See #225
introduce os.EnvMap10 files changed, 171 insertions(+), 206 deletions(-)
doc/langref.md-13| ... | ... | @@ -295,19 +295,6 @@ has a terminating null byte. |
| 295 | 295 | Built-in functions are prefixed with `@`. Remember that the `comptime` keyword on |
| 296 | 296 | a parameter means that the parameter must be known at compile time. |
| 297 | 297 | |
| 298 | ### @alloca(comptime T: type, count: usize) -> []T | |
| 299 | ||
| 300 | Allocates memory in the stack frame of the caller. This temporary space is | |
| 301 | automatically freed when the function that called alloca returns to its caller, | |
| 302 | just like other stack variables. | |
| 303 | ||
| 304 | When using this function to allocate memory, you should know the upper bound | |
| 305 | of `count`. Consider putting a constant array on the stack with the upper bound | |
| 306 | instead of using alloca. If you do use alloca it is to save a few bytes off | |
| 307 | the memory size given that you didn't actually hit your upper bound. | |
| 308 | ||
| 309 | The allocated memory contents are undefined. | |
| 310 | ||
| 311 | 298 | ### @typeOf(expression) -> type |
| 312 | 299 | |
| 313 | 300 | This function returns a compile-time constant, which is the type of the |
src/all_types.hpp-10| ... | ... | @@ -1190,7 +1190,6 @@ enum BuiltinFnId { |
| 1190 | 1190 | BuiltinFnIdTruncate, |
| 1191 | 1191 | BuiltinFnIdIntType, |
| 1192 | 1192 | BuiltinFnIdSetDebugSafety, |
| 1193 | BuiltinFnIdAlloca, | |
| 1194 | 1193 | BuiltinFnIdTypeName, |
| 1195 | 1194 | BuiltinFnIdIsInteger, |
| 1196 | 1195 | BuiltinFnIdIsFloat, |
| ... | ... | @@ -1706,7 +1705,6 @@ enum IrInstructionId { |
| 1706 | 1705 | IrInstructionIdTruncate, |
| 1707 | 1706 | IrInstructionIdIntType, |
| 1708 | 1707 | IrInstructionIdBoolNot, |
| 1709 | IrInstructionIdAlloca, | |
| 1710 | 1708 | IrInstructionIdMemset, |
| 1711 | 1709 | IrInstructionIdMemcpy, |
| 1712 | 1710 | IrInstructionIdSlice, |
| ... | ... | @@ -2234,14 +2232,6 @@ struct IrInstructionBoolNot { |
| 2234 | 2232 | IrInstruction *value; |
| 2235 | 2233 | }; |
| 2236 | 2234 | |
| 2237 | struct IrInstructionAlloca { | |
| 2238 | IrInstruction base; | |
| 2239 | ||
| 2240 | IrInstruction *type_value; | |
| 2241 | IrInstruction *count; | |
| 2242 | LLVMValueRef tmp_ptr; | |
| 2243 | }; | |
| 2244 | ||
| 2245 | 2235 | struct IrInstructionMemset { |
| 2246 | 2236 | IrInstruction base; |
| 2247 | 2237 |
src/codegen.cpp+1-27| ... | ... | @@ -2190,26 +2190,6 @@ static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrI |
| 2190 | 2190 | } |
| 2191 | 2191 | } |
| 2192 | 2192 | |
| 2193 | static LLVMValueRef ir_render_alloca(CodeGen *g, IrExecutable *executable, IrInstructionAlloca *instruction) { | |
| 2194 | TypeTableEntry *slice_type = get_underlying_type(instruction->base.value.type); | |
| 2195 | TypeTableEntry *ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; | |
| 2196 | TypeTableEntry *child_type = ptr_type->data.pointer.child_type; | |
| 2197 | LLVMValueRef size_val = ir_llvm_value(g, instruction->count); | |
| 2198 | LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref, size_val, ""); | |
| 2199 | ||
| 2200 | // TODO in debug mode, initialize all the bytes to 0xaa | |
| 2201 | ||
| 2202 | // store the freshly allocated pointer in the slice | |
| 2203 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_ptr_index, ""); | |
| 2204 | LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr); | |
| 2205 | ||
| 2206 | // store the size in the len field | |
| 2207 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_len_index, ""); | |
| 2208 | LLVMBuildStore(g->builder, size_val, len_field_ptr); | |
| 2209 | ||
| 2210 | return instruction->tmp_ptr; | |
| 2211 | } | |
| 2212 | ||
| 2213 | 2193 | static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrInstructionMemset *instruction) { |
| 2214 | 2194 | LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr); |
| 2215 | 2195 | LLVMValueRef char_val = ir_llvm_value(g, instruction->byte); |
| ... | ... | @@ -2548,7 +2528,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I |
| 2548 | 2528 | assert(instruction->tmp_ptr); |
| 2549 | 2529 | |
| 2550 | 2530 | LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); |
| 2551 | assert(child_type == instruction->value->value.type); | |
| 2531 | // child_type and instruction->value->value.type may differ by constness | |
| 2552 | 2532 | gen_assign_raw(g, val_ptr, payload_val, child_type); |
| 2553 | 2533 | LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); |
| 2554 | 2534 | LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); |
| ... | ... | @@ -2796,8 +2776,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 2796 | 2776 | return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction); |
| 2797 | 2777 | case IrInstructionIdBoolNot: |
| 2798 | 2778 | return ir_render_bool_not(g, executable, (IrInstructionBoolNot *)instruction); |
| 2799 | case IrInstructionIdAlloca: | |
| 2800 | return ir_render_alloca(g, executable, (IrInstructionAlloca *)instruction); | |
| 2801 | 2779 | case IrInstructionIdMemset: |
| 2802 | 2780 | return ir_render_memset(g, executable, (IrInstructionMemset *)instruction); |
| 2803 | 2781 | case IrInstructionIdMemcpy: |
| ... | ... | @@ -3648,9 +3626,6 @@ static void do_code_gen(CodeGen *g) { |
| 3648 | 3626 | } else if (instruction->id == IrInstructionIdCall) { |
| 3649 | 3627 | IrInstructionCall *call_instruction = (IrInstructionCall *)instruction; |
| 3650 | 3628 | slot = &call_instruction->tmp_ptr; |
| 3651 | } else if (instruction->id == IrInstructionIdAlloca) { | |
| 3652 | IrInstructionAlloca *alloca_instruction = (IrInstructionAlloca *)instruction; | |
| 3653 | slot = &alloca_instruction->tmp_ptr; | |
| 3654 | 3629 | } else if (instruction->id == IrInstructionIdSlice) { |
| 3655 | 3630 | IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; |
| 3656 | 3631 | slot = &slice_instruction->tmp_ptr; |
| ... | ... | @@ -4326,7 +4301,6 @@ static void define_builtin_fns(CodeGen *g) { |
| 4326 | 4301 | create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); |
| 4327 | 4302 | create_builtin_fn(g, BuiltinFnIdIntType, "intType", 2); |
| 4328 | 4303 | create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2); |
| 4329 | create_builtin_fn(g, BuiltinFnIdAlloca, "alloca", 2); | |
| 4330 | 4304 | create_builtin_fn(g, BuiltinFnIdSetGlobalAlign, "setGlobalAlign", 2); |
| 4331 | 4305 | create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2); |
| 4332 | 4306 | create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2); |
src/ir.cpp-77| ... | ... | @@ -404,10 +404,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) { |
| 404 | 404 | return IrInstructionIdBoolNot; |
| 405 | 405 | } |
| 406 | 406 | |
| 407 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) { | |
| 408 | return IrInstructionIdAlloca; | |
| 409 | } | |
| 410 | ||
| 411 | 407 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) { |
| 412 | 408 | return IrInstructionIdMemset; |
| 413 | 409 | } |
| ... | ... | @@ -1668,27 +1664,6 @@ static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_ |
| 1668 | 1664 | return new_instruction; |
| 1669 | 1665 | } |
| 1670 | 1666 | |
| 1671 | static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 1672 | IrInstruction *type_value, IrInstruction *count) | |
| 1673 | { | |
| 1674 | IrInstructionAlloca *instruction = ir_build_instruction<IrInstructionAlloca>(irb, scope, source_node); | |
| 1675 | instruction->type_value = type_value; | |
| 1676 | instruction->count = count; | |
| 1677 | ||
| 1678 | ir_ref_instruction(type_value, irb->current_basic_block); | |
| 1679 | ir_ref_instruction(count, irb->current_basic_block); | |
| 1680 | ||
| 1681 | return &instruction->base; | |
| 1682 | } | |
| 1683 | ||
| 1684 | static IrInstruction *ir_build_alloca_from(IrBuilder *irb, IrInstruction *old_instruction, | |
| 1685 | IrInstruction *type_value, IrInstruction *count) | |
| 1686 | { | |
| 1687 | IrInstruction *new_instruction = ir_build_alloca(irb, old_instruction->scope, old_instruction->source_node, type_value, count); | |
| 1688 | ir_link_new_instruction(new_instruction, old_instruction); | |
| 1689 | return new_instruction; | |
| 1690 | } | |
| 1691 | ||
| 1692 | 1667 | static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 1693 | 1668 | IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count) |
| 1694 | 1669 | { |
| ... | ... | @@ -2567,14 +2542,6 @@ static IrInstruction *ir_instruction_boolnot_get_dep(IrInstructionBoolNot *instr |
| 2567 | 2542 | } |
| 2568 | 2543 | } |
| 2569 | 2544 | |
| 2570 | static IrInstruction *ir_instruction_alloca_get_dep(IrInstructionAlloca *instruction, size_t index) { | |
| 2571 | switch (index) { | |
| 2572 | case 0: return instruction->type_value; | |
| 2573 | case 1: return instruction->count; | |
| 2574 | default: return nullptr; | |
| 2575 | } | |
| 2576 | } | |
| 2577 | ||
| 2578 | 2545 | static IrInstruction *ir_instruction_memset_get_dep(IrInstructionMemset *instruction, size_t index) { |
| 2579 | 2546 | switch (index) { |
| 2580 | 2547 | case 0: return instruction->dest_ptr; |
| ... | ... | @@ -2938,8 +2905,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 2938 | 2905 | return ir_instruction_inttype_get_dep((IrInstructionIntType *) instruction, index); |
| 2939 | 2906 | case IrInstructionIdBoolNot: |
| 2940 | 2907 | return ir_instruction_boolnot_get_dep((IrInstructionBoolNot *) instruction, index); |
| 2941 | case IrInstructionIdAlloca: | |
| 2942 | return ir_instruction_alloca_get_dep((IrInstructionAlloca *) instruction, index); | |
| 2943 | 2908 | case IrInstructionIdMemset: |
| 2944 | 2909 | return ir_instruction_memset_get_dep((IrInstructionMemset *) instruction, index); |
| 2945 | 2910 | case IrInstructionIdMemcpy: |
| ... | ... | @@ -4100,20 +4065,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4100 | 4065 | |
| 4101 | 4066 | return ir_build_int_type(irb, scope, node, arg0_value, arg1_value); |
| 4102 | 4067 | } |
| 4103 | case BuiltinFnIdAlloca: | |
| 4104 | { | |
| 4105 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4106 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 4107 | if (arg0_value == irb->codegen->invalid_instruction) | |
| 4108 | return arg0_value; | |
| 4109 | ||
| 4110 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4111 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 4112 | if (arg1_value == irb->codegen->invalid_instruction) | |
| 4113 | return arg1_value; | |
| 4114 | ||
| 4115 | return ir_build_alloca(irb, scope, node, arg0_value, arg1_value); | |
| 4116 | } | |
| 4117 | 4068 | case BuiltinFnIdMemcpy: |
| 4118 | 4069 | { |
| 4119 | 4070 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -11461,31 +11412,6 @@ static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstruc |
| 11461 | 11412 | return bool_type; |
| 11462 | 11413 | } |
| 11463 | 11414 | |
| 11464 | static TypeTableEntry *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) { | |
| 11465 | IrInstruction *type_value = instruction->type_value->other; | |
| 11466 | if (type_is_invalid(type_value->value.type)) | |
| 11467 | return ira->codegen->builtin_types.entry_invalid; | |
| 11468 | ||
| 11469 | IrInstruction *count_value = instruction->count->other; | |
| 11470 | if (type_is_invalid(count_value->value.type)) | |
| 11471 | return ira->codegen->builtin_types.entry_invalid; | |
| 11472 | ||
| 11473 | TypeTableEntry *child_type = ir_resolve_type(ira, type_value); | |
| 11474 | ||
| 11475 | if (type_requires_comptime(child_type)) { | |
| 11476 | ir_add_error(ira, type_value, | |
| 11477 | buf_sprintf("invalid alloca type '%s'", buf_ptr(&child_type->name))); | |
| 11478 | // TODO if this is a typedecl, add error note showing the declaration of the type decl | |
| 11479 | return ira->codegen->builtin_types.entry_invalid; | |
| 11480 | } else { | |
| 11481 | TypeTableEntry *slice_type = get_slice_type(ira->codegen, child_type, false); | |
| 11482 | IrInstruction *new_instruction = ir_build_alloca_from(&ira->new_irb, &instruction->base, type_value, count_value); | |
| 11483 | ir_add_alloca(ira, new_instruction, slice_type); | |
| 11484 | return slice_type; | |
| 11485 | } | |
| 11486 | zig_unreachable(); | |
| 11487 | } | |
| 11488 | ||
| 11489 | 11415 | static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) { |
| 11490 | 11416 | IrInstruction *dest_ptr = instruction->dest_ptr->other; |
| 11491 | 11417 | if (type_is_invalid(dest_ptr->value.type)) |
| ... | ... | @@ -12550,8 +12476,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 12550 | 12476 | return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); |
| 12551 | 12477 | case IrInstructionIdBoolNot: |
| 12552 | 12478 | return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction); |
| 12553 | case IrInstructionIdAlloca: | |
| 12554 | return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction); | |
| 12555 | 12479 | case IrInstructionIdMemset: |
| 12556 | 12480 | return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction); |
| 12557 | 12481 | case IrInstructionIdMemcpy: |
| ... | ... | @@ -12749,7 +12673,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 12749 | 12673 | case IrInstructionIdTruncate: |
| 12750 | 12674 | case IrInstructionIdIntType: |
| 12751 | 12675 | case IrInstructionIdBoolNot: |
| 12752 | case IrInstructionIdAlloca: | |
| 12753 | 12676 | case IrInstructionIdSlice: |
| 12754 | 12677 | case IrInstructionIdMemberCount: |
| 12755 | 12678 | case IrInstructionIdAlignOf: |
src/ir_print.cpp-11| ... | ... | @@ -601,14 +601,6 @@ static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction) |
| 601 | 601 | fprintf(irp->f, ")"); |
| 602 | 602 | } |
| 603 | 603 | |
| 604 | static void ir_print_alloca(IrPrint *irp, IrInstructionAlloca *instruction) { | |
| 605 | fprintf(irp->f, "@alloca("); | |
| 606 | ir_print_other_instruction(irp, instruction->type_value); | |
| 607 | fprintf(irp->f, ", "); | |
| 608 | ir_print_other_instruction(irp, instruction->count); | |
| 609 | fprintf(irp->f, ")"); | |
| 610 | } | |
| 611 | ||
| 612 | 604 | static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) { |
| 613 | 605 | fprintf(irp->f, "@intType("); |
| 614 | 606 | ir_print_other_instruction(irp, instruction->is_signed); |
| ... | ... | @@ -1049,9 +1041,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1049 | 1041 | case IrInstructionIdTruncate: |
| 1050 | 1042 | ir_print_truncate(irp, (IrInstructionTruncate *)instruction); |
| 1051 | 1043 | break; |
| 1052 | case IrInstructionIdAlloca: | |
| 1053 | ir_print_alloca(irp, (IrInstructionAlloca *)instruction); | |
| 1054 | break; | |
| 1055 | 1044 | case IrInstructionIdIntType: |
| 1056 | 1045 | ir_print_int_type(irp, (IrInstructionIntType *)instruction); |
| 1057 | 1046 | break; |
std/build.zig+3-1| ... | ... | @@ -40,6 +40,8 @@ pub const Builder = struct { |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | 42 | pub fn make(self: &Builder, cli_args: []const []const u8) -> %void { |
| 43 | var env_map = %return os.getEnvMap(self.allocator); | |
| 44 | ||
| 43 | 45 | var verbose = false; |
| 44 | 46 | for (cli_args) |arg| { |
| 45 | 47 | if (mem.eql(u8, arg, "--verbose")) { |
| ... | ... | @@ -93,7 +95,7 @@ pub const Builder = struct { |
| 93 | 95 | } |
| 94 | 96 | |
| 95 | 97 | printInvocation(self.zig_exe, zig_args); |
| 96 | var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), os.environ, | |
| 98 | var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), env_map, | |
| 97 | 99 | StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator); |
| 98 | 100 | const term = %return child.wait(); |
| 99 | 101 | switch (term) { |
std/hash_map.zig+12-7| ... | ... | @@ -29,7 +29,7 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 29 | 29 | }; |
| 30 | 30 | |
| 31 | 31 | pub const Iterator = struct { |
| 32 | hm: &Self, | |
| 32 | hm: &const Self, | |
| 33 | 33 | // how many items have we returned |
| 34 | 34 | count: usize, |
| 35 | 35 | // iterator through the entry array |
| ... | ... | @@ -99,17 +99,21 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 99 | 99 | } |
| 100 | 100 | |
| 101 | 101 | pub fn get(hm: &Self, key: K) -> ?&Entry { |
| 102 | if (hm.entries.len == 0) { | |
| 103 | return null; | |
| 104 | } | |
| 102 | 105 | return hm.internalGet(key); |
| 103 | 106 | } |
| 104 | 107 | |
| 105 | pub fn remove(hm: &Self, key: K) { | |
| 108 | pub fn remove(hm: &Self, key: K) -> ?&Entry { | |
| 106 | 109 | hm.incrementModificationCount(); |
| 107 | 110 | const start_index = hm.keyToIndex(key); |
| 108 | 111 | {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) { |
| 109 | 112 | const index = (start_index + roll_over) % hm.entries.len; |
| 110 | 113 | var entry = &hm.entries[index]; |
| 111 | 114 | |
| 112 | assert(entry.used); // key not found | |
| 115 | if (!entry.used) | |
| 116 | return null; | |
| 113 | 117 | |
| 114 | 118 | if (!eql(entry.key, key)) continue; |
| 115 | 119 | |
| ... | ... | @@ -119,7 +123,7 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 119 | 123 | if (!next_entry.used or next_entry.distance_from_start_index == 0) { |
| 120 | 124 | entry.used = false; |
| 121 | 125 | hm.size -= 1; |
| 122 | return; | |
| 126 | return entry; | |
| 123 | 127 | } |
| 124 | 128 | *entry = *next_entry; |
| 125 | 129 | entry.distance_from_start_index -= 1; |
| ... | ... | @@ -127,10 +131,10 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 127 | 131 | } |
| 128 | 132 | unreachable // shifting everything in the table |
| 129 | 133 | }} |
| 130 | unreachable // key not found | |
| 134 | return null; | |
| 131 | 135 | } |
| 132 | 136 | |
| 133 | pub fn entryIterator(hm: &Self) -> Iterator { | |
| 137 | pub fn entryIterator(hm: &const Self) -> Iterator { | |
| 134 | 138 | return Iterator { |
| 135 | 139 | .hm = hm, |
| 136 | 140 | .count = 0, |
| ... | ... | @@ -231,7 +235,8 @@ test "basicHashMapTest" { |
| 231 | 235 | %%map.put(5, 55); |
| 232 | 236 | |
| 233 | 237 | assert((??map.get(2)).value == 22); |
| 234 | map.remove(2); | |
| 238 | _ = map.remove(2); | |
| 239 | assert(map.remove(2) == null); | |
| 235 | 240 | assert(if (const entry ?= map.get(2)) false else true); |
| 236 | 241 | } |
| 237 | 242 |
std/os/index.zig+135-23| ... | ... | @@ -21,6 +21,8 @@ const mem = @import("../mem.zig"); |
| 21 | 21 | const Allocator = mem.Allocator; |
| 22 | 22 | |
| 23 | 23 | const io = @import("../io.zig"); |
| 24 | const HashMap = @import("../hash_map.zig").HashMap; | |
| 25 | const cstr = @import("../cstr.zig"); | |
| 24 | 26 | |
| 25 | 27 | error Unexpected; |
| 26 | 28 | error SysResources; |
| ... | ... | @@ -284,12 +286,12 @@ pub const ChildProcess = struct { |
| 284 | 286 | Close, |
| 285 | 287 | }; |
| 286 | 288 | |
| 287 | pub fn spawn(exe_path: []const u8, args: []const []const u8, env: []const EnvPair, | |
| 289 | pub fn spawn(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap, | |
| 288 | 290 | stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess |
| 289 | 291 | { |
| 290 | 292 | switch (@compileVar("os")) { |
| 291 | 293 | Os.linux, Os.macosx, Os.ios, Os.darwin => { |
| 292 | return spawnPosix(exe_path, args, env, stdin, stdout, stderr, allocator); | |
| 294 | return spawnPosix(exe_path, args, env_map, stdin, stdout, stderr, allocator); | |
| 293 | 295 | }, |
| 294 | 296 | else => @compileError("Unsupported OS"), |
| 295 | 297 | } |
| ... | ... | @@ -351,7 +353,7 @@ pub const ChildProcess = struct { |
| 351 | 353 | }; |
| 352 | 354 | } |
| 353 | 355 | |
| 354 | fn spawnPosix(exe_path: []const u8, args: []const []const u8, env: []const EnvPair, | |
| 356 | fn spawnPosix(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap, | |
| 355 | 357 | stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess |
| 356 | 358 | { |
| 357 | 359 | // TODO issue #295 |
| ... | ... | @@ -408,7 +410,7 @@ pub const ChildProcess = struct { |
| 408 | 410 | setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %% |
| 409 | 411 | |err| forkChildErrReport(err_pipe[1], err); |
| 410 | 412 | |
| 411 | const err = posix.getErrno(%return execve(exe_path, args, env, allocator)); | |
| 413 | const err = posix.getErrno(%return execve(exe_path, args, env_map, allocator)); | |
| 412 | 414 | assert(err > 0); |
| 413 | 415 | forkChildErrReport(err_pipe[1], switch (err) { |
| 414 | 416 | errno.EFAULT => unreachable, |
| ... | ... | @@ -473,7 +475,7 @@ pub const ChildProcess = struct { |
| 473 | 475 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null |
| 474 | 476 | /// pointers after the args and after the environment variables. |
| 475 | 477 | /// Also make the first arg equal to path. |
| 476 | fn execve(path: []const u8, argv: []const []const u8, envp: []const EnvPair, allocator: &Allocator) -> %usize { | |
| 478 | fn execve(path: []const u8, argv: []const []const u8, env_map: &const EnvMap, allocator: &Allocator) -> %usize { | |
| 477 | 479 | const path_buf = %return allocator.alloc(u8, path.len + 1); |
| 478 | 480 | defer allocator.free(path_buf); |
| 479 | 481 | @memcpy(&path_buf[0], &path[0], path.len); |
| ... | ... | @@ -505,39 +507,149 @@ fn execve(path: []const u8, argv: []const []const u8, envp: []const EnvPair, all |
| 505 | 507 | } |
| 506 | 508 | argv_buf[argv.len + 1] = null; |
| 507 | 509 | |
| 508 | const envp_buf = %return allocator.alloc(?&const u8, envp.len + 1); | |
| 510 | const envp_count = env_map.count(); | |
| 511 | const envp_buf = %return allocator.alloc(?&const u8, envp_count + 1); | |
| 509 | 512 | mem.set(?&const u8, envp_buf, null); |
| 510 | 513 | defer { |
| 511 | 514 | for (envp_buf) |env, i| { |
| 512 | const env_buf = if (const ptr ?= env) ptr[0...envp[i].key.len + envp[i].value.len + 2] else break; | |
| 515 | const env_buf = if (const ptr ?= env) ptr[0...cstr.len(ptr)] else break; | |
| 513 | 516 | allocator.free(env_buf); |
| 514 | 517 | } |
| 515 | 518 | allocator.free(envp_buf); |
| 516 | 519 | } |
| 517 | for (envp) |pair, i| { | |
| 518 | const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2); | |
| 519 | @memcpy(&env_buf[0], pair.key.ptr, pair.key.len); | |
| 520 | env_buf[pair.key.len] = '='; | |
| 521 | @memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); | |
| 522 | env_buf[env_buf.len - 1] = 0; | |
| 523 | ||
| 524 | envp_buf[i] = env_buf.ptr; | |
| 520 | { | |
| 521 | var it = env_map.iterator(); | |
| 522 | var i: usize = 0; | |
| 523 | while (true; i += 1) { | |
| 524 | const pair = it.next() ?? break; | |
| 525 | ||
| 526 | const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2); | |
| 527 | @memcpy(&env_buf[0], pair.key.ptr, pair.key.len); | |
| 528 | env_buf[pair.key.len] = '='; | |
| 529 | @memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); | |
| 530 | env_buf[env_buf.len - 1] = 0; | |
| 531 | ||
| 532 | envp_buf[i] = env_buf.ptr; | |
| 533 | } | |
| 534 | assert(i == envp_count); | |
| 525 | 535 | } |
| 526 | envp_buf[envp.len] = null; | |
| 536 | envp_buf[envp_count] = null; | |
| 527 | 537 | |
| 528 | 538 | return posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr); |
| 529 | 539 | } |
| 530 | 540 | |
| 531 | pub const EnvPair = struct { | |
| 532 | key: []const u8, | |
| 533 | value: []const u8, | |
| 541 | pub var environ_raw: []&u8 = undefined; | |
| 542 | ||
| 543 | pub const EnvMap = struct { | |
| 544 | hash_map: EnvHashMap, | |
| 545 | ||
| 546 | const EnvHashMap = HashMap([]const u8, []const u8, hash_slice_u8, eql_slice_u8); | |
| 547 | ||
| 548 | pub fn init(allocator: &Allocator) -> EnvMap { | |
| 549 | var self = EnvMap { | |
| 550 | .hash_map = undefined, | |
| 551 | }; | |
| 552 | self.hash_map.init(allocator); | |
| 553 | return self; | |
| 554 | } | |
| 555 | ||
| 556 | pub fn deinit(self: &EnvMap) { | |
| 557 | var it = self.hash_map.entryIterator(); | |
| 558 | while (true) { | |
| 559 | const entry = it.next() ?? break; | |
| 560 | self.free(entry.key); | |
| 561 | self.free(entry.value); | |
| 562 | } | |
| 563 | ||
| 564 | self.hash_map.deinit(); | |
| 565 | } | |
| 566 | ||
| 567 | pub fn set(self: &EnvMap, key: []const u8, value: []const u8) -> %void { | |
| 568 | if (const entry ?= self.hash_map.get(key)) { | |
| 569 | const value_copy = %return self.copy(value); | |
| 570 | %defer self.free(value_copy); | |
| 571 | %return self.hash_map.put(key, value_copy); | |
| 572 | self.free(entry.value); | |
| 573 | } else { | |
| 574 | const key_copy = %return self.copy(key); | |
| 575 | %defer self.free(key_copy); | |
| 576 | const value_copy = %return self.copy(value); | |
| 577 | %defer self.free(value_copy); | |
| 578 | %return self.hash_map.put(key_copy, value_copy); | |
| 579 | } | |
| 580 | } | |
| 581 | ||
| 582 | pub fn delete(self: &EnvMap, key: []const u8) { | |
| 583 | const entry = self.hash_map.remove(key) ?? return; | |
| 584 | self.free(entry.key); | |
| 585 | self.free(entry.value); | |
| 586 | } | |
| 587 | ||
| 588 | pub fn count(self: &const EnvMap) -> usize { | |
| 589 | return self.hash_map.size; | |
| 590 | } | |
| 591 | ||
| 592 | pub fn iterator(self: &const EnvMap) -> EnvHashMap.Iterator { | |
| 593 | return self.hash_map.entryIterator(); | |
| 594 | } | |
| 595 | ||
| 596 | fn free(self: &EnvMap, value: []const u8) { | |
| 597 | // remove the const | |
| 598 | const mut_value = @ptrcast(&u8, value.ptr)[0...value.len]; | |
| 599 | self.hash_map.allocator.free(mut_value); | |
| 600 | } | |
| 601 | ||
| 602 | fn copy(self: &EnvMap, value: []const u8) -> %[]const u8 { | |
| 603 | const result = %return self.hash_map.allocator.alloc(u8, value.len); | |
| 604 | mem.copy(u8, result, value); | |
| 605 | return result; | |
| 606 | } | |
| 534 | 607 | }; |
| 535 | pub var environ: []const EnvPair = undefined; | |
| 608 | ||
| 609 | pub fn getEnvMap(allocator: &Allocator) -> %EnvMap { | |
| 610 | var result = EnvMap.init(allocator); | |
| 611 | %defer result.deinit(); | |
| 612 | ||
| 613 | for (environ_raw) |ptr| { | |
| 614 | var line_i: usize = 0; | |
| 615 | while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {} | |
| 616 | const key = ptr[0...line_i]; | |
| 617 | ||
| 618 | var end_i: usize = line_i; | |
| 619 | while (ptr[end_i] != 0; end_i += 1) {} | |
| 620 | const value = ptr[line_i + 1...end_i]; | |
| 621 | ||
| 622 | %return result.set(key, value); | |
| 623 | } | |
| 624 | return result; | |
| 625 | } | |
| 536 | 626 | |
| 537 | 627 | pub fn getEnv(key: []const u8) -> ?[]const u8 { |
| 538 | for (environ) |pair| { | |
| 539 | if (mem.eql(u8, pair.key, key)) | |
| 540 | return pair.value; | |
| 628 | for (environ_raw) |ptr| { | |
| 629 | var line_i: usize = 0; | |
| 630 | while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {} | |
| 631 | const this_key = ptr[0...line_i]; | |
| 632 | if (!mem.eql(u8, key, this_key)) | |
| 633 | continue; | |
| 634 | ||
| 635 | var end_i: usize = line_i; | |
| 636 | while (ptr[end_i] != 0; end_i += 1) {} | |
| 637 | const this_value = ptr[line_i + 1...end_i]; | |
| 638 | ||
| 639 | return this_value; | |
| 541 | 640 | } |
| 542 | 641 | return null; |
| 543 | 642 | } |
| 643 | ||
| 644 | fn hash_slice_u8(k: []const u8) -> u32 { | |
| 645 | // FNV 32-bit hash | |
| 646 | var h: u32 = 2166136261; | |
| 647 | for (k) |b| { | |
| 648 | h = (h ^ b) *% 16777619; | |
| 649 | } | |
| 650 | return h; | |
| 651 | } | |
| 652 | ||
| 653 | fn eql_slice_u8(a: []const u8, b: []const u8) -> bool { | |
| 654 | return mem.eql(u8, a, b); | |
| 655 | } |
std/special/bootstrap.zig+17-35| ... | ... | @@ -9,8 +9,7 @@ const want_start_symbol = !want_main_symbol; |
| 9 | 9 | |
| 10 | 10 | const exit = std.os.posix.exit; |
| 11 | 11 | |
| 12 | var argc: usize = undefined; | |
| 13 | var argv: &&u8 = undefined; | |
| 12 | var argc_ptr: &usize = undefined; | |
| 14 | 13 | |
| 15 | 14 | export nakedcc fn _start() -> noreturn { |
| 16 | 15 | @setGlobalLinkage(_start, if (want_start_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal); |
| ... | ... | @@ -20,21 +19,28 @@ export nakedcc fn _start() -> noreturn { |
| 20 | 19 | |
| 21 | 20 | switch (@compileVar("arch")) { |
| 22 | 21 | Arch.x86_64 => { |
| 23 | argc = asm("mov %[argc], [rsp]": [argc] "=r" (-> usize)); | |
| 24 | argv = asm("lea %[argv], [rsp + 8h]": [argv] "=r" (-> &&u8)); | |
| 22 | argc_ptr = asm("lea %[argc], [rsp]": [argc] "=r" (-> &usize)); | |
| 25 | 23 | }, |
| 26 | 24 | Arch.i386 => { |
| 27 | argc = asm("mov %[argc], [esp]": [argc] "=r" (-> usize)); | |
| 28 | argv = asm("lea %[argv], [esp + 4h]": [argv] "=r" (-> &&u8)); | |
| 25 | argc_ptr = asm("lea %[argc], [esp]": [argc] "=r" (-> &usize)); | |
| 29 | 26 | }, |
| 30 | 27 | else => @compileError("unsupported arch"), |
| 31 | 28 | } |
| 32 | 29 | callMainAndExit() |
| 33 | 30 | } |
| 34 | 31 | |
| 35 | fn callMain(envp: &?&u8) -> %void { | |
| 36 | // TODO issue #225 | |
| 37 | const args = @alloca([]u8, argc); | |
| 32 | fn callMainAndExit() -> noreturn { | |
| 33 | const argc = *argc_ptr; | |
| 34 | const argv = @ptrcast(&&u8, &argc_ptr[1]); | |
| 35 | const envp = @ptrcast(&?&u8, &argv[argc + 1]); | |
| 36 | callMain(argc, argv, envp) %% exit(1); | |
| 37 | exit(0); | |
| 38 | } | |
| 39 | ||
| 40 | var args_data: [32][]u8 = undefined; | |
| 41 | fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void { | |
| 42 | // TODO create args API to make it work with > 32 args | |
| 43 | const args = args_data[0...argc]; | |
| 38 | 44 | for (args) |_, i| { |
| 39 | 45 | const ptr = argv[i]; |
| 40 | 46 | args[i] = ptr[0...std.cstr.len(ptr)]; |
| ... | ... | @@ -42,41 +48,17 @@ fn callMain(envp: &?&u8) -> %void { |
| 42 | 48 | |
| 43 | 49 | var env_count: usize = 0; |
| 44 | 50 | while (envp[env_count] != null; env_count += 1) {} |
| 45 | // TODO issue #225 | |
| 46 | const environ = @alloca(std.os.EnvPair, env_count); | |
| 47 | for (environ) |_, env_i| { | |
| 48 | const ptr = ??envp[env_i]; | |
| 49 | ||
| 50 | var line_i: usize = 0; | |
| 51 | while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {} | |
| 52 | ||
| 53 | var end_i: usize = line_i; | |
| 54 | while (ptr[end_i] != 0; end_i += 1) {} | |
| 55 | ||
| 56 | environ[env_i] = std.os.EnvPair { | |
| 57 | .key = ptr[0...line_i], | |
| 58 | .value = ptr[line_i + 1...end_i], | |
| 59 | }; | |
| 60 | } | |
| 61 | std.os.environ = environ; | |
| 51 | std.os.environ_raw = @ptrcast(&&u8, envp)[0...env_count]; | |
| 62 | 52 | |
| 63 | 53 | return root.main(args); |
| 64 | 54 | } |
| 65 | 55 | |
| 66 | fn callMainAndExit() -> noreturn { | |
| 67 | const envp = @ptrcast(&?&u8, &argv[argc + 1]); | |
| 68 | callMain(envp) %% exit(1); | |
| 69 | exit(0); | |
| 70 | } | |
| 71 | ||
| 72 | 56 | export fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 { |
| 73 | 57 | @setGlobalLinkage(main, if (want_main_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal); |
| 74 | 58 | if (!want_main_symbol) { |
| 75 | 59 | unreachable; |
| 76 | 60 | } |
| 77 | 61 | |
| 78 | argc = usize(c_argc); | |
| 79 | argv = c_argv; | |
| 80 | callMain(c_envp) %% return 1; | |
| 62 | callMain(usize(c_argc), c_argv, c_envp) %% return 1; | |
| 81 | 63 | return 0; |
| 82 | 64 | } |
test/cases/const_slice_child.zig+3-2| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | const assert = @import("std").debug.assert; | |
| 1 | const debug = @import("std").debug; | |
| 2 | const assert = debug.assert; | |
| 2 | 3 | |
| 3 | 4 | var argv: &const &const u8 = undefined; |
| 4 | 5 | |
| ... | ... | @@ -20,7 +21,7 @@ fn foo(args: [][]const u8) { |
| 20 | 21 | } |
| 21 | 22 | |
| 22 | 23 | fn bar(argc: usize) { |
| 23 | const args = @alloca([]u8, argc); | |
| 24 | const args = %%debug.global_allocator.alloc([]u8, argc); | |
| 24 | 25 | for (args) |_, i| { |
| 25 | 26 | const ptr = argv[i]; |
| 26 | 27 | args[i] = ptr[0...strlen(ptr)]; |