parent
c400cb429a
commit
c9ae30d27e
|
@ -295,19 +295,6 @@ has a terminating null byte.
|
|||
Built-in functions are prefixed with `@`. Remember that the `comptime` keyword on
|
||||
a parameter means that the parameter must be known at compile time.
|
||||
|
||||
### @alloca(comptime T: type, count: usize) -> []T
|
||||
|
||||
Allocates memory in the stack frame of the caller. This temporary space is
|
||||
automatically freed when the function that called alloca returns to its caller,
|
||||
just like other stack variables.
|
||||
|
||||
When using this function to allocate memory, you should know the upper bound
|
||||
of `count`. Consider putting a constant array on the stack with the upper bound
|
||||
instead of using alloca. If you do use alloca it is to save a few bytes off
|
||||
the memory size given that you didn't actually hit your upper bound.
|
||||
|
||||
The allocated memory contents are undefined.
|
||||
|
||||
### @typeOf(expression) -> type
|
||||
|
||||
This function returns a compile-time constant, which is the type of the
|
||||
|
|
|
@ -1190,7 +1190,6 @@ enum BuiltinFnId {
|
|||
BuiltinFnIdTruncate,
|
||||
BuiltinFnIdIntType,
|
||||
BuiltinFnIdSetDebugSafety,
|
||||
BuiltinFnIdAlloca,
|
||||
BuiltinFnIdTypeName,
|
||||
BuiltinFnIdIsInteger,
|
||||
BuiltinFnIdIsFloat,
|
||||
|
@ -1706,7 +1705,6 @@ enum IrInstructionId {
|
|||
IrInstructionIdTruncate,
|
||||
IrInstructionIdIntType,
|
||||
IrInstructionIdBoolNot,
|
||||
IrInstructionIdAlloca,
|
||||
IrInstructionIdMemset,
|
||||
IrInstructionIdMemcpy,
|
||||
IrInstructionIdSlice,
|
||||
|
@ -2234,14 +2232,6 @@ struct IrInstructionBoolNot {
|
|||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionAlloca {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type_value;
|
||||
IrInstruction *count;
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionMemset {
|
||||
IrInstruction base;
|
||||
|
||||
|
|
|
@ -2190,26 +2190,6 @@ static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrI
|
|||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_alloca(CodeGen *g, IrExecutable *executable, IrInstructionAlloca *instruction) {
|
||||
TypeTableEntry *slice_type = get_underlying_type(instruction->base.value.type);
|
||||
TypeTableEntry *ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||
TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
|
||||
LLVMValueRef size_val = ir_llvm_value(g, instruction->count);
|
||||
LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref, size_val, "");
|
||||
|
||||
// TODO in debug mode, initialize all the bytes to 0xaa
|
||||
|
||||
// store the freshly allocated pointer in the slice
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_ptr_index, "");
|
||||
LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
|
||||
|
||||
// store the size in the len field
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_len_index, "");
|
||||
LLVMBuildStore(g->builder, size_val, len_field_ptr);
|
||||
|
||||
return instruction->tmp_ptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrInstructionMemset *instruction) {
|
||||
LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr);
|
||||
LLVMValueRef char_val = ir_llvm_value(g, instruction->byte);
|
||||
|
@ -2548,7 +2528,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I
|
|||
assert(instruction->tmp_ptr);
|
||||
|
||||
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
|
||||
assert(child_type == instruction->value->value.type);
|
||||
// child_type and instruction->value->value.type may differ by constness
|
||||
gen_assign_raw(g, val_ptr, payload_val, child_type);
|
||||
LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, "");
|
||||
LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
|
||||
|
@ -2796,8 +2776,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
|||
return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);
|
||||
case IrInstructionIdBoolNot:
|
||||
return ir_render_bool_not(g, executable, (IrInstructionBoolNot *)instruction);
|
||||
case IrInstructionIdAlloca:
|
||||
return ir_render_alloca(g, executable, (IrInstructionAlloca *)instruction);
|
||||
case IrInstructionIdMemset:
|
||||
return ir_render_memset(g, executable, (IrInstructionMemset *)instruction);
|
||||
case IrInstructionIdMemcpy:
|
||||
|
@ -3648,9 +3626,6 @@ static void do_code_gen(CodeGen *g) {
|
|||
} else if (instruction->id == IrInstructionIdCall) {
|
||||
IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
|
||||
slot = &call_instruction->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdAlloca) {
|
||||
IrInstructionAlloca *alloca_instruction = (IrInstructionAlloca *)instruction;
|
||||
slot = &alloca_instruction->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdSlice) {
|
||||
IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction;
|
||||
slot = &slice_instruction->tmp_ptr;
|
||||
|
@ -4326,7 +4301,6 @@ static void define_builtin_fns(CodeGen *g) {
|
|||
create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdIntType, "intType", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdAlloca, "alloca", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalAlign, "setGlobalAlign", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2);
|
||||
|
|
77
src/ir.cpp
77
src/ir.cpp
|
@ -404,10 +404,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
|
|||
return IrInstructionIdBoolNot;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) {
|
||||
return IrInstructionIdAlloca;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) {
|
||||
return IrInstructionIdMemset;
|
||||
}
|
||||
|
@ -1668,27 +1664,6 @@ static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_
|
|||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *type_value, IrInstruction *count)
|
||||
{
|
||||
IrInstructionAlloca *instruction = ir_build_instruction<IrInstructionAlloca>(irb, scope, source_node);
|
||||
instruction->type_value = type_value;
|
||||
instruction->count = count;
|
||||
|
||||
ir_ref_instruction(type_value, irb->current_basic_block);
|
||||
ir_ref_instruction(count, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_alloca_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *type_value, IrInstruction *count)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_alloca(irb, old_instruction->scope, old_instruction->source_node, type_value, count);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count)
|
||||
{
|
||||
|
@ -2567,14 +2542,6 @@ static IrInstruction *ir_instruction_boolnot_get_dep(IrInstructionBoolNot *instr
|
|||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_alloca_get_dep(IrInstructionAlloca *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->type_value;
|
||||
case 1: return instruction->count;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_memset_get_dep(IrInstructionMemset *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->dest_ptr;
|
||||
|
@ -2938,8 +2905,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
|||
return ir_instruction_inttype_get_dep((IrInstructionIntType *) instruction, index);
|
||||
case IrInstructionIdBoolNot:
|
||||
return ir_instruction_boolnot_get_dep((IrInstructionBoolNot *) instruction, index);
|
||||
case IrInstructionIdAlloca:
|
||||
return ir_instruction_alloca_get_dep((IrInstructionAlloca *) instruction, index);
|
||||
case IrInstructionIdMemset:
|
||||
return ir_instruction_memset_get_dep((IrInstructionMemset *) instruction, index);
|
||||
case IrInstructionIdMemcpy:
|
||||
|
@ -4100,20 +4065,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
|||
|
||||
return ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
case BuiltinFnIdAlloca:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
||||
if (arg0_value == irb->codegen->invalid_instruction)
|
||||
return arg0_value;
|
||||
|
||||
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
||||
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
||||
if (arg1_value == irb->codegen->invalid_instruction)
|
||||
return arg1_value;
|
||||
|
||||
return ir_build_alloca(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
case BuiltinFnIdMemcpy:
|
||||
{
|
||||
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
|
|||
return bool_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) {
|
||||
IrInstruction *type_value = instruction->type_value->other;
|
||||
if (type_is_invalid(type_value->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *count_value = instruction->count->other;
|
||||
if (type_is_invalid(count_value->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
TypeTableEntry *child_type = ir_resolve_type(ira, type_value);
|
||||
|
||||
if (type_requires_comptime(child_type)) {
|
||||
ir_add_error(ira, type_value,
|
||||
buf_sprintf("invalid alloca type '%s'", buf_ptr(&child_type->name)));
|
||||
// TODO if this is a typedecl, add error note showing the declaration of the type decl
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
} else {
|
||||
TypeTableEntry *slice_type = get_slice_type(ira->codegen, child_type, false);
|
||||
IrInstruction *new_instruction = ir_build_alloca_from(&ira->new_irb, &instruction->base, type_value, count_value);
|
||||
ir_add_alloca(ira, new_instruction, slice_type);
|
||||
return slice_type;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) {
|
||||
IrInstruction *dest_ptr = instruction->dest_ptr->other;
|
||||
if (type_is_invalid(dest_ptr->value.type))
|
||||
|
@ -12550,8 +12476,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
|||
return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
|
||||
case IrInstructionIdBoolNot:
|
||||
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
|
||||
case IrInstructionIdAlloca:
|
||||
return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction);
|
||||
case IrInstructionIdMemset:
|
||||
return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction);
|
||||
case IrInstructionIdMemcpy:
|
||||
|
@ -12749,7 +12673,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||
case IrInstructionIdTruncate:
|
||||
case IrInstructionIdIntType:
|
||||
case IrInstructionIdBoolNot:
|
||||
case IrInstructionIdAlloca:
|
||||
case IrInstructionIdSlice:
|
||||
case IrInstructionIdMemberCount:
|
||||
case IrInstructionIdAlignOf:
|
||||
|
|
|
@ -601,14 +601,6 @@ static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction)
|
|||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_alloca(IrPrint *irp, IrInstructionAlloca *instruction) {
|
||||
fprintf(irp->f, "@alloca(");
|
||||
ir_print_other_instruction(irp, instruction->type_value);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->count);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) {
|
||||
fprintf(irp->f, "@intType(");
|
||||
ir_print_other_instruction(irp, instruction->is_signed);
|
||||
|
@ -1049,9 +1041,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
|||
case IrInstructionIdTruncate:
|
||||
ir_print_truncate(irp, (IrInstructionTruncate *)instruction);
|
||||
break;
|
||||
case IrInstructionIdAlloca:
|
||||
ir_print_alloca(irp, (IrInstructionAlloca *)instruction);
|
||||
break;
|
||||
case IrInstructionIdIntType:
|
||||
ir_print_int_type(irp, (IrInstructionIntType *)instruction);
|
||||
break;
|
||||
|
|
|
@ -40,6 +40,8 @@ pub const Builder = struct {
|
|||
}
|
||||
|
||||
pub fn make(self: &Builder, cli_args: []const []const u8) -> %void {
|
||||
var env_map = %return os.getEnvMap(self.allocator);
|
||||
|
||||
var verbose = false;
|
||||
for (cli_args) |arg| {
|
||||
if (mem.eql(u8, arg, "--verbose")) {
|
||||
|
@ -93,7 +95,7 @@ pub const Builder = struct {
|
|||
}
|
||||
|
||||
printInvocation(self.zig_exe, zig_args);
|
||||
var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), os.environ,
|
||||
var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), env_map,
|
||||
StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator);
|
||||
const term = %return child.wait();
|
||||
switch (term) {
|
||||
|
|
|
@ -29,7 +29,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
|||
};
|
||||
|
||||
pub const Iterator = struct {
|
||||
hm: &Self,
|
||||
hm: &const Self,
|
||||
// how many items have we returned
|
||||
count: usize,
|
||||
// iterator through the entry array
|
||||
|
@ -99,17 +99,21 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
|||
}
|
||||
|
||||
pub fn get(hm: &Self, key: K) -> ?&Entry {
|
||||
if (hm.entries.len == 0) {
|
||||
return null;
|
||||
}
|
||||
return hm.internalGet(key);
|
||||
}
|
||||
|
||||
pub fn remove(hm: &Self, key: K) {
|
||||
pub fn remove(hm: &Self, key: K) -> ?&Entry {
|
||||
hm.incrementModificationCount();
|
||||
const start_index = hm.keyToIndex(key);
|
||||
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) {
|
||||
const index = (start_index + roll_over) % hm.entries.len;
|
||||
var entry = &hm.entries[index];
|
||||
|
||||
assert(entry.used); // key not found
|
||||
if (!entry.used)
|
||||
return null;
|
||||
|
||||
if (!eql(entry.key, key)) continue;
|
||||
|
||||
|
@ -119,7 +123,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
|||
if (!next_entry.used or next_entry.distance_from_start_index == 0) {
|
||||
entry.used = false;
|
||||
hm.size -= 1;
|
||||
return;
|
||||
return entry;
|
||||
}
|
||||
*entry = *next_entry;
|
||||
entry.distance_from_start_index -= 1;
|
||||
|
@ -127,10 +131,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
|||
}
|
||||
unreachable // shifting everything in the table
|
||||
}}
|
||||
unreachable // key not found
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn entryIterator(hm: &Self) -> Iterator {
|
||||
pub fn entryIterator(hm: &const Self) -> Iterator {
|
||||
return Iterator {
|
||||
.hm = hm,
|
||||
.count = 0,
|
||||
|
@ -231,7 +235,8 @@ test "basicHashMapTest" {
|
|||
%%map.put(5, 55);
|
||||
|
||||
assert((??map.get(2)).value == 22);
|
||||
map.remove(2);
|
||||
_ = map.remove(2);
|
||||
assert(map.remove(2) == null);
|
||||
assert(if (const entry ?= map.get(2)) false else true);
|
||||
}
|
||||
|
||||
|
|
156
std/os/index.zig
156
std/os/index.zig
|
@ -21,6 +21,8 @@ const mem = @import("../mem.zig");
|
|||
const Allocator = mem.Allocator;
|
||||
|
||||
const io = @import("../io.zig");
|
||||
const HashMap = @import("../hash_map.zig").HashMap;
|
||||
const cstr = @import("../cstr.zig");
|
||||
|
||||
error Unexpected;
|
||||
error SysResources;
|
||||
|
@ -284,12 +286,12 @@ pub const ChildProcess = struct {
|
|||
Close,
|
||||
};
|
||||
|
||||
pub fn spawn(exe_path: []const u8, args: []const []const u8, env: []const EnvPair,
|
||||
pub fn spawn(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap,
|
||||
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
|
||||
{
|
||||
switch (@compileVar("os")) {
|
||||
Os.linux, Os.macosx, Os.ios, Os.darwin => {
|
||||
return spawnPosix(exe_path, args, env, stdin, stdout, stderr, allocator);
|
||||
return spawnPosix(exe_path, args, env_map, stdin, stdout, stderr, allocator);
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
|
@ -351,7 +353,7 @@ pub const ChildProcess = struct {
|
|||
};
|
||||
}
|
||||
|
||||
fn spawnPosix(exe_path: []const u8, args: []const []const u8, env: []const EnvPair,
|
||||
fn spawnPosix(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap,
|
||||
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
|
||||
{
|
||||
// TODO issue #295
|
||||
|
@ -408,7 +410,7 @@ pub const ChildProcess = struct {
|
|||
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
|
||||
const err = posix.getErrno(%return execve(exe_path, args, env, allocator));
|
||||
const err = posix.getErrno(%return execve(exe_path, args, env_map, allocator));
|
||||
assert(err > 0);
|
||||
forkChildErrReport(err_pipe[1], switch (err) {
|
||||
errno.EFAULT => unreachable,
|
||||
|
@ -473,7 +475,7 @@ pub const ChildProcess = struct {
|
|||
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
|
||||
/// pointers after the args and after the environment variables.
|
||||
/// Also make the first arg equal to path.
|
||||
fn execve(path: []const u8, argv: []const []const u8, envp: []const EnvPair, allocator: &Allocator) -> %usize {
|
||||
fn execve(path: []const u8, argv: []const []const u8, env_map: &const EnvMap, allocator: &Allocator) -> %usize {
|
||||
const path_buf = %return allocator.alloc(u8, path.len + 1);
|
||||
defer allocator.free(path_buf);
|
||||
@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
|
|||
}
|
||||
argv_buf[argv.len + 1] = null;
|
||||
|
||||
const envp_buf = %return allocator.alloc(?&const u8, envp.len + 1);
|
||||
const envp_count = env_map.count();
|
||||
const envp_buf = %return allocator.alloc(?&const u8, envp_count + 1);
|
||||
mem.set(?&const u8, envp_buf, null);
|
||||
defer {
|
||||
for (envp_buf) |env, i| {
|
||||
const env_buf = if (const ptr ?= env) ptr[0...envp[i].key.len + envp[i].value.len + 2] else break;
|
||||
const env_buf = if (const ptr ?= env) ptr[0...cstr.len(ptr)] else break;
|
||||
allocator.free(env_buf);
|
||||
}
|
||||
allocator.free(envp_buf);
|
||||
}
|
||||
for (envp) |pair, i| {
|
||||
const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2);
|
||||
@memcpy(&env_buf[0], pair.key.ptr, pair.key.len);
|
||||
env_buf[pair.key.len] = '=';
|
||||
@memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len);
|
||||
env_buf[env_buf.len - 1] = 0;
|
||||
{
|
||||
var it = env_map.iterator();
|
||||
var i: usize = 0;
|
||||
while (true; i += 1) {
|
||||
const pair = it.next() ?? break;
|
||||
|
||||
envp_buf[i] = env_buf.ptr;
|
||||
const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2);
|
||||
@memcpy(&env_buf[0], pair.key.ptr, pair.key.len);
|
||||
env_buf[pair.key.len] = '=';
|
||||
@memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len);
|
||||
env_buf[env_buf.len - 1] = 0;
|
||||
|
||||
envp_buf[i] = env_buf.ptr;
|
||||
}
|
||||
assert(i == envp_count);
|
||||
}
|
||||
envp_buf[envp.len] = null;
|
||||
envp_buf[envp_count] = null;
|
||||
|
||||
return posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr);
|
||||
}
|
||||
|
||||
pub const EnvPair = struct {
|
||||
key: []const u8,
|
||||
value: []const u8,
|
||||
pub var environ_raw: []&u8 = undefined;
|
||||
|
||||
pub const EnvMap = struct {
|
||||
hash_map: EnvHashMap,
|
||||
|
||||
const EnvHashMap = HashMap([]const u8, []const u8, hash_slice_u8, eql_slice_u8);
|
||||
|
||||
pub fn init(allocator: &Allocator) -> EnvMap {
|
||||
var self = EnvMap {
|
||||
.hash_map = undefined,
|
||||
};
|
||||
self.hash_map.init(allocator);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: &EnvMap) {
|
||||
var it = self.hash_map.entryIterator();
|
||||
while (true) {
|
||||
const entry = it.next() ?? break;
|
||||
self.free(entry.key);
|
||||
self.free(entry.value);
|
||||
}
|
||||
|
||||
self.hash_map.deinit();
|
||||
}
|
||||
|
||||
pub fn set(self: &EnvMap, key: []const u8, value: []const u8) -> %void {
|
||||
if (const entry ?= self.hash_map.get(key)) {
|
||||
const value_copy = %return self.copy(value);
|
||||
%defer self.free(value_copy);
|
||||
%return self.hash_map.put(key, value_copy);
|
||||
self.free(entry.value);
|
||||
} else {
|
||||
const key_copy = %return self.copy(key);
|
||||
%defer self.free(key_copy);
|
||||
const value_copy = %return self.copy(value);
|
||||
%defer self.free(value_copy);
|
||||
%return self.hash_map.put(key_copy, value_copy);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete(self: &EnvMap, key: []const u8) {
|
||||
const entry = self.hash_map.remove(key) ?? return;
|
||||
self.free(entry.key);
|
||||
self.free(entry.value);
|
||||
}
|
||||
|
||||
pub fn count(self: &const EnvMap) -> usize {
|
||||
return self.hash_map.size;
|
||||
}
|
||||
|
||||
pub fn iterator(self: &const EnvMap) -> EnvHashMap.Iterator {
|
||||
return self.hash_map.entryIterator();
|
||||
}
|
||||
|
||||
fn free(self: &EnvMap, value: []const u8) {
|
||||
// remove the const
|
||||
const mut_value = @ptrcast(&u8, value.ptr)[0...value.len];
|
||||
self.hash_map.allocator.free(mut_value);
|
||||
}
|
||||
|
||||
fn copy(self: &EnvMap, value: []const u8) -> %[]const u8 {
|
||||
const result = %return self.hash_map.allocator.alloc(u8, value.len);
|
||||
mem.copy(u8, result, value);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
pub var environ: []const EnvPair = undefined;
|
||||
|
||||
pub fn getEnvMap(allocator: &Allocator) -> %EnvMap {
|
||||
var result = EnvMap.init(allocator);
|
||||
%defer result.deinit();
|
||||
|
||||
for (environ_raw) |ptr| {
|
||||
var line_i: usize = 0;
|
||||
while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {}
|
||||
const key = ptr[0...line_i];
|
||||
|
||||
var end_i: usize = line_i;
|
||||
while (ptr[end_i] != 0; end_i += 1) {}
|
||||
const value = ptr[line_i + 1...end_i];
|
||||
|
||||
%return result.set(key, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn getEnv(key: []const u8) -> ?[]const u8 {
|
||||
for (environ) |pair| {
|
||||
if (mem.eql(u8, pair.key, key))
|
||||
return pair.value;
|
||||
for (environ_raw) |ptr| {
|
||||
var line_i: usize = 0;
|
||||
while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {}
|
||||
const this_key = ptr[0...line_i];
|
||||
if (!mem.eql(u8, key, this_key))
|
||||
continue;
|
||||
|
||||
var end_i: usize = line_i;
|
||||
while (ptr[end_i] != 0; end_i += 1) {}
|
||||
const this_value = ptr[line_i + 1...end_i];
|
||||
|
||||
return this_value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn hash_slice_u8(k: []const u8) -> u32 {
|
||||
// FNV 32-bit hash
|
||||
var h: u32 = 2166136261;
|
||||
for (k) |b| {
|
||||
h = (h ^ b) *% 16777619;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fn eql_slice_u8(a: []const u8, b: []const u8) -> bool {
|
||||
return mem.eql(u8, a, b);
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@ const want_start_symbol = !want_main_symbol;
|
|||
|
||||
const exit = std.os.posix.exit;
|
||||
|
||||
var argc: usize = undefined;
|
||||
var argv: &&u8 = undefined;
|
||||
var argc_ptr: &usize = undefined;
|
||||
|
||||
export nakedcc fn _start() -> noreturn {
|
||||
@setGlobalLinkage(_start, if (want_start_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal);
|
||||
|
@ -20,21 +19,28 @@ export nakedcc fn _start() -> noreturn {
|
|||
|
||||
switch (@compileVar("arch")) {
|
||||
Arch.x86_64 => {
|
||||
argc = asm("mov %[argc], [rsp]": [argc] "=r" (-> usize));
|
||||
argv = asm("lea %[argv], [rsp + 8h]": [argv] "=r" (-> &&u8));
|
||||
argc_ptr = asm("lea %[argc], [rsp]": [argc] "=r" (-> &usize));
|
||||
},
|
||||
Arch.i386 => {
|
||||
argc = asm("mov %[argc], [esp]": [argc] "=r" (-> usize));
|
||||
argv = asm("lea %[argv], [esp + 4h]": [argv] "=r" (-> &&u8));
|
||||
argc_ptr = asm("lea %[argc], [esp]": [argc] "=r" (-> &usize));
|
||||
},
|
||||
else => @compileError("unsupported arch"),
|
||||
}
|
||||
callMainAndExit()
|
||||
}
|
||||
|
||||
fn callMain(envp: &?&u8) -> %void {
|
||||
// TODO issue #225
|
||||
const args = @alloca([]u8, argc);
|
||||
fn callMainAndExit() -> noreturn {
|
||||
const argc = *argc_ptr;
|
||||
const argv = @ptrcast(&&u8, &argc_ptr[1]);
|
||||
const envp = @ptrcast(&?&u8, &argv[argc + 1]);
|
||||
callMain(argc, argv, envp) %% exit(1);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
var args_data: [32][]u8 = undefined;
|
||||
fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
|
||||
// TODO create args API to make it work with > 32 args
|
||||
const args = args_data[0...argc];
|
||||
for (args) |_, i| {
|
||||
const ptr = argv[i];
|
||||
args[i] = ptr[0...std.cstr.len(ptr)];
|
||||
|
@ -42,41 +48,17 @@ fn callMain(envp: &?&u8) -> %void {
|
|||
|
||||
var env_count: usize = 0;
|
||||
while (envp[env_count] != null; env_count += 1) {}
|
||||
// TODO issue #225
|
||||
const environ = @alloca(std.os.EnvPair, env_count);
|
||||
for (environ) |_, env_i| {
|
||||
const ptr = ??envp[env_i];
|
||||
|
||||
var line_i: usize = 0;
|
||||
while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {}
|
||||
|
||||
var end_i: usize = line_i;
|
||||
while (ptr[end_i] != 0; end_i += 1) {}
|
||||
|
||||
environ[env_i] = std.os.EnvPair {
|
||||
.key = ptr[0...line_i],
|
||||
.value = ptr[line_i + 1...end_i],
|
||||
};
|
||||
}
|
||||
std.os.environ = environ;
|
||||
std.os.environ_raw = @ptrcast(&&u8, envp)[0...env_count];
|
||||
|
||||
return root.main(args);
|
||||
}
|
||||
|
||||
fn callMainAndExit() -> noreturn {
|
||||
const envp = @ptrcast(&?&u8, &argv[argc + 1]);
|
||||
callMain(envp) %% exit(1);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
export fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
|
||||
@setGlobalLinkage(main, if (want_main_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal);
|
||||
if (!want_main_symbol) {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
argc = usize(c_argc);
|
||||
argv = c_argv;
|
||||
callMain(c_envp) %% return 1;
|
||||
callMain(usize(c_argc), c_argv, c_envp) %% return 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const assert = @import("std").debug.assert;
|
||||
const debug = @import("std").debug;
|
||||
const assert = debug.assert;
|
||||
|
||||
var argv: &const &const u8 = undefined;
|
||||
|
||||
|
@ -20,7 +21,7 @@ fn foo(args: [][]const u8) {
|
|||
}
|
||||
|
||||
fn bar(argc: usize) {
|
||||
const args = @alloca([]u8, argc);
|
||||
const args = %%debug.global_allocator.alloc([]u8, argc);
|
||||
for (args) |_, i| {
|
||||
const ptr = argv[i];
|
||||
args[i] = ptr[0...strlen(ptr)];
|
||||
|
|
Loading…
Reference in New Issue