1use super::promise;
2use crate::host::HostFunction;
3use crate::object::function::JSFunction;
4use crate::object::object::JSObject;
5use crate::runtime::context::JSContext;
6use crate::value::JSValue;
7
8pub fn init_process_module(ctx: &mut JSContext) {
9 let specifier = "pipa:process";
10
11 ctx.register_builtin("process_exit", HostFunction::new("exit", 1, process_exit));
12 ctx.register_builtin("process_cwd", HostFunction::new("cwd", 0, process_cwd));
13 ctx.register_builtin("process_exec", HostFunction::new("exec", 2, process_exec));
14
15 let mut module = crate::runtime::module::Module::new(specifier.to_string(), String::new());
16
17 module.add_export("argv".to_string(), make_argv(ctx), false);
18 module.add_export(
19 "argc".to_string(),
20 JSValue::new_int(ctx.runtime().argv.len() as i64),
21 false,
22 );
23 module.add_export("env".to_string(), make_env(ctx), false);
24
25 let (cwd_fn, exit_fn, exec_fn) = make_functions(ctx);
26 module.add_export("cwd".to_string(), cwd_fn, false);
27 module.add_export("exit".to_string(), exit_fn, false);
28 module.add_export("exec".to_string(), exec_fn, false);
29
30 ctx.runtime_mut().module_registry_mut().register(module);
31}
32
33fn make_argv(ctx: &mut JSContext) -> JSValue {
34 let args: Vec<String> = ctx.runtime().argv.clone();
35 let mut arr = JSObject::new_array();
36 if let Some(proto_ptr) = ctx.get_array_prototype() {
37 arr.prototype = Some(proto_ptr);
38 }
39 arr.set(ctx.common_atoms.length, JSValue::new_int(args.len() as i64));
40
41 for (i, arg) in args.iter().enumerate() {
42 let val = JSValue::new_string(ctx.intern(arg));
43 let atom = ctx.intern(&i.to_string());
44 arr.set(atom, val);
45 }
46
47 let ptr = Box::into_raw(Box::new(arr)) as usize;
48 JSValue::new_object(ptr)
49}
50
51fn make_env(ctx: &mut JSContext) -> JSValue {
52 let mut obj = JSObject::new();
53 for (key, val) in std::env::vars() {
54 let key_atom = ctx.intern(&key);
55 obj.set(key_atom, JSValue::new_string(ctx.intern(&val)));
56 }
57 let ptr = Box::into_raw(Box::new(obj)) as usize;
58 JSValue::new_object(ptr)
59}
60
61fn make_functions(ctx: &mut JSContext) -> (JSValue, JSValue, JSValue) {
62 let cwd = make_function_value(ctx, "cwd", 0, "process_cwd");
63 let exit = make_function_value(ctx, "exit", 1, "process_exit");
64 let exec = make_function_value(ctx, "exec", 2, "process_exec");
65 (cwd, exit, exec)
66}
67
68fn make_function_value(ctx: &mut JSContext, name: &str, arity: u32, marker: &str) -> JSValue {
69 let mut func = JSFunction::new_builtin(ctx.intern(name), arity);
70 func.set_builtin_marker(ctx, marker);
71 let ptr = Box::into_raw(Box::new(func)) as usize;
72 ctx.runtime_mut().gc_heap_mut().track_function(ptr);
73 JSValue::new_function(ptr)
74}
75
76fn process_exit(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
77 let code = if args.is_empty() || !args[0].is_int() {
78 0
79 } else {
80 args[0].get_int() as i32
81 };
82 std::process::exit(code);
83}
84
85fn process_cwd(ctx: &mut JSContext, _args: &[JSValue]) -> JSValue {
86 match std::env::current_dir() {
87 Ok(p) => JSValue::new_string(ctx.intern(&p.to_string_lossy().to_string())),
88 Err(e) => {
89 let s = format!("{}", e);
90 let msg = JSValue::new_string(ctx.intern(&s));
91 promise::create_rejected_promise(ctx, msg)
92 }
93 }
94}
95
96fn process_exec(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
97 let start_idx = if args.len() >= 3 { 1 } else { 0 };
98 let real_args = &args[start_idx..];
99
100 if real_args.is_empty() || !real_args[0].is_string() {
101 let msg = JSValue::new_string(ctx.intern("exec: command must be a string"));
102 return promise::create_rejected_promise(ctx, msg);
103 }
104
105 let command = ctx.get_atom_str(real_args[0].get_atom()).to_string();
106
107 let cmd_args: Vec<String> = parse_js_array(ctx, real_args.get(1));
108
109 let result = crate::runtime::process_task::run_command_sync(&command, &cmd_args);
110
111 let mut promise_obj = JSObject::new_promise();
112 if let Some(proto_ptr) = ctx.get_promise_prototype() {
113 promise_obj.prototype = Some(proto_ptr);
114 }
115 promise_obj.set(ctx.intern("__promise_state__"), JSValue::new_int(0));
116 promise_obj.set(ctx.intern("__promise_result__"), JSValue::undefined());
117 promise_obj.set(ctx.intern("__promise_reactions__"), JSValue::null());
118 let promise_ptr = Box::into_raw(Box::new(promise_obj)) as usize;
119 let promise_val = JSValue::new_object(promise_ptr);
120
121 if let Some(e) = result.error {
122 let msg = JSValue::new_string(ctx.intern(&e));
123 super::promise::reject_promise_with_value(ctx, promise_ptr, msg);
124 } else {
125 let mut result_obj = crate::object::object::JSObject::new();
126 result_obj.set(
127 ctx.intern("stdout"),
128 JSValue::new_string(ctx.intern(&String::from_utf8_lossy(&result.stdout))),
129 );
130 result_obj.set(
131 ctx.intern("stderr"),
132 JSValue::new_string(ctx.intern(&String::from_utf8_lossy(&result.stderr))),
133 );
134 result_obj.set(ctx.intern("code"), JSValue::new_int(result.code as i64));
135 result_obj.set(
136 ctx.intern("signal"),
137 if let Some(sig) = result.signal {
138 JSValue::new_int(sig as i64)
139 } else {
140 JSValue::null()
141 },
142 );
143 let result_ptr = Box::into_raw(Box::new(result_obj)) as usize;
144 super::promise::fulfill_promise_with_value(
145 ctx,
146 promise_ptr,
147 JSValue::new_object(result_ptr),
148 );
149 }
150
151 promise_val
152}
153
154fn parse_js_array(ctx: &mut JSContext, arg: Option<&JSValue>) -> Vec<String> {
155 let obj_val = match arg {
156 Some(v) if v.is_object() => v,
157 _ => return Vec::new(),
158 };
159 let obj = obj_val.as_object();
160
161 let len = if obj.is_dense_array() {
162 let arr_ptr = obj_val.get_ptr() as *const crate::object::array_obj::JSArrayObject;
163 unsafe { &*arr_ptr }.len()
164 } else {
165 let len_val = obj.get(ctx.intern("length")).unwrap_or(JSValue::new_int(0));
166 if len_val.is_int() {
167 len_val.get_int() as usize
168 } else {
169 0
170 }
171 };
172
173 let mut result = Vec::with_capacity(len);
174 for i in 0..len {
175 if obj.is_dense_array() {
176 let arr_ptr = obj_val.get_ptr() as *const crate::object::array_obj::JSArrayObject;
177 let arr = unsafe { &*arr_ptr };
178 if let Some(val) = arr.get(i) {
179 if val.is_string() {
180 result.push(ctx.get_atom_str(val.get_atom()).to_string());
181 }
182 }
183 } else {
184 let atom = ctx.intern(&i.to_string());
185 if let Some(val) = obj.get(atom) {
186 if val.is_string() {
187 result.push(ctx.get_atom_str(val.get_atom()).to_string());
188 }
189 }
190 }
191 }
192 result
193}