v.gen.js, os_js: port the OS module to JS (#10872)
parent
6313ed6a79
commit
69cbdf9fdc
|
@ -104,6 +104,13 @@ pub fn (mut a array) insert(i int, val voidptr) {
|
|||
#a.arr.splice(i,0,val)
|
||||
}
|
||||
|
||||
pub fn (mut a array) join(separator string) string {
|
||||
mut res := ''
|
||||
#res = new builtin.string(a.arr.join(separator +''));
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn (a array) push(val voidptr) {
|
||||
#a.arr.push(val)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
module os_js
|
||||
|
||||
// setenv sets the value of an environment variable with `name` to `value`.
|
||||
pub fn setenv(key string, val string, overwrite bool) {
|
||||
#if ($process.env[key] && !(overwrite.valueOf())) return;
|
||||
#$process.env[key] = val + '';
|
||||
}
|
||||
|
||||
// `getenv` returns the value of the environment variable named by the key.
|
||||
pub fn getenv(key string) string {
|
||||
mut res := ''
|
||||
#if ($process.env[key]) res = new builtin.string($process.env[key])
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// unsetenv clears an environment variable with `name`.
|
||||
pub fn unsetenv(name string) int {
|
||||
#$process.env[name] = ""
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
pub fn environ() map[string]string {
|
||||
mut res := map[string]string{}
|
||||
#for (const key in $process.env) {
|
||||
#res.map.set(key,$process.env[key])
|
||||
#}
|
||||
|
||||
return res
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
module os_js
|
||||
|
||||
pub struct File {
|
||||
pub:
|
||||
fd int
|
||||
pub mut:
|
||||
is_opened bool
|
||||
}
|
||||
|
||||
#const $buffer = require('buffer');
|
||||
|
||||
// todo(playX): __as_cast is broken here
|
||||
/*
|
||||
pub struct ErrFileNotOpened {
|
||||
msg string = 'os: file not opened'
|
||||
code int
|
||||
}
|
||||
pub struct ErrSizeOfTypeIs0 {
|
||||
msg string = 'os: size of type is 0'
|
||||
code int
|
||||
}
|
||||
fn error_file_not_opened() IError {
|
||||
return IError(&ErrFileNotOpened{})
|
||||
}
|
||||
fn error_size_of_type_0() IError {
|
||||
return IError(&ErrSizeOfTypeIs0{})
|
||||
}
|
||||
*/
|
||||
pub fn open_file(path string, mode string, options ...int) ?File {
|
||||
mut res := File{}
|
||||
$if js_node {
|
||||
#if (!options) { options = new array([]); }
|
||||
#let permissions = 0o666
|
||||
#if (options.arr.length > 0) { permissions = options.arr[0]; }
|
||||
#try {
|
||||
#res.fd = new int($fs.openSync(''+path,''+mode,permissions))
|
||||
#} catch (e) {
|
||||
#return builtin.error('' + e);
|
||||
#}
|
||||
|
||||
res.is_opened = true
|
||||
} $else {
|
||||
error('cannot open file on non NodeJS runtime')
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// open tries to open a file for reading and returns back a read-only `File` object.
|
||||
pub fn open(path string) ?File {
|
||||
f := open_file(path, 'r') ?
|
||||
return f
|
||||
}
|
||||
|
||||
pub fn create(path string) ?File {
|
||||
f := open_file(path, 'w') ?
|
||||
return f
|
||||
}
|
||||
|
||||
pub fn stdin() File {
|
||||
return File{
|
||||
fd: 0
|
||||
is_opened: true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stdout() File {
|
||||
return File{
|
||||
fd: 1
|
||||
is_opened: true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stderr() File {
|
||||
return File{
|
||||
fd: 2
|
||||
is_opened: true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (f &File) read(mut buf []byte) ?int {
|
||||
if buf.len == 0 {
|
||||
return 0
|
||||
}
|
||||
mut nbytes := 0
|
||||
#try {
|
||||
#let buffer = $fs.readFileSync(f.fd.valueOf());
|
||||
#
|
||||
#for (const val of buffer.values()) { buf.arr[nbytes++] = val; }
|
||||
#}
|
||||
#catch (e) { return builtin.error('' + e); }
|
||||
|
||||
return nbytes
|
||||
}
|
||||
|
||||
pub fn (mut f File) write(buf []byte) ?int {
|
||||
if !f.is_opened {
|
||||
return error('file is not opened')
|
||||
}
|
||||
mut nbytes := 0
|
||||
#const b = $buffer.Buffer.from(buf.arr.map((x) => x.valueOf()))
|
||||
#try { $fs.writeSync(f.fd.valueOf(),b,0,buf.len.valueOf(),0); } catch (e) { return builtin.error('' + e); }
|
||||
|
||||
return nbytes
|
||||
}
|
||||
|
||||
// writeln writes the string `s` into the file, and appends a \n character.
|
||||
// It returns how many bytes were written, including the \n character.
|
||||
pub fn (mut f File) writeln(s string) ?int {
|
||||
mut nbytes := f.write(s.bytes()) ?
|
||||
nbytes += f.write('\n'.bytes()) ?
|
||||
return nbytes
|
||||
}
|
||||
|
||||
pub fn (mut f File) write_to(pos u64, buf []byte) ?int {
|
||||
if !f.is_opened {
|
||||
return error('file is not opened')
|
||||
}
|
||||
mut nbytes := 0
|
||||
#const b = $buffer.Buffer.from(buf.arr.map((x) => x.valueOf()))
|
||||
#try { $fs.writeSync(f.fd.valueOf(),b,0,buf.len.valueOf(),pos.valueOf()); } catch (e) { return builtin.error('' + e); }
|
||||
|
||||
return nbytes
|
||||
}
|
||||
|
||||
// write_string writes the string `s` into the file
|
||||
// It returns how many bytes were actually written.
|
||||
pub fn (mut f File) write_string(s string) ?int {
|
||||
nbytes := f.write(s.bytes()) ?
|
||||
return nbytes
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
module os_js
|
||||
|
||||
#const $fs = require('fs');
|
||||
#const $path = require('path');
|
||||
|
||||
pub const (
|
||||
args = []string{}
|
||||
)
|
||||
|
||||
$if js_node {
|
||||
#$process.argv.forEach(function(val,index) { args.arr[index] = new string(val); })
|
||||
}
|
||||
|
||||
// real_path returns the full absolute path for fpath, with all relative ../../, symlinks and so on resolved.
|
||||
// See http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
|
||||
// Also https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
|
||||
// and https://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html
|
||||
// NB: this particular rabbit hole is *deep* ...
|
||||
pub fn real_path(fpath string) string {
|
||||
$if js_node {
|
||||
mut res := ''
|
||||
#res = new string( $fs.realpathSync(fpath))
|
||||
|
||||
return res
|
||||
} $else {
|
||||
return fpath
|
||||
}
|
||||
}
|
||||
|
||||
// flush will flush the stdout buffer.
|
||||
pub fn flush() {
|
||||
$if js_node {
|
||||
#$process.stdout.write('')
|
||||
}
|
||||
}
|
||||
|
||||
// chmod change file access attributes of `path` to `mode`.
|
||||
// Octals like `0o600` can be used.
|
||||
pub fn chmod(path string, mode int) {
|
||||
$if js_node {
|
||||
#$fs.chmodSync(''+path,mode.valueOf())
|
||||
}
|
||||
}
|
||||
|
||||
// chown changes the owner and group attributes of `path` to `owner` and `group`.
|
||||
// Octals like `0o600` can be used.
|
||||
pub fn chown(path string, owner int, group int) {
|
||||
$if js_node {
|
||||
#$fs.chownSync(''+path,owner.valueOf(),group.valueOf())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn temp_dir() string {
|
||||
mut res := ''
|
||||
$if js_node {
|
||||
#res = new builtin.string($os.tmpdir())
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn home_dir() string {
|
||||
mut res := ''
|
||||
$if js_node {
|
||||
#res = new builtin.string($os.homedir())
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// join_path returns a path as string from input string parameter(s).
|
||||
pub fn join_path(base string, dirs ...string) string {
|
||||
mut result := []string{}
|
||||
result << base.trim_right('\\/')
|
||||
for d in dirs {
|
||||
result << d
|
||||
}
|
||||
mut path_sep := ''
|
||||
#path_sep = $path.sep;
|
||||
|
||||
res := result.join(path_sep)
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn execute(cmd string) Result {
|
||||
mut exit_code := 0
|
||||
mut stdout := ''
|
||||
#let commands = cmd.str.split(' ');
|
||||
#let output = $child_process.spawnSync(commands[0],commands.slice(1,commands.length));
|
||||
#exit_code = new builtin.int(output.status)
|
||||
#stdout = new builtin.string(output.stdout + '')
|
||||
|
||||
return Result{
|
||||
exit_code: exit_code
|
||||
output: stdout
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
module os_js
|
||||
|
||||
pub const (
|
||||
// todo(playX): NodeJS does not seem to have any path limit?
|
||||
max_path_len = 4096
|
||||
)
|
||||
|
||||
pub struct Result {
|
||||
pub:
|
||||
exit_code int
|
||||
output string
|
||||
// stderr string // TODO
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
module os_js
|
||||
|
||||
#const $child_process = require('child_process')
|
||||
|
||||
// ProcessState.not_started - the process has not yet started
|
||||
// ProcessState.running - the process is currently running
|
||||
// ProcessState.stopped - the process was running, but was stopped temporarily
|
||||
// ProcessState.exited - the process has finished/exited
|
||||
// ProcessState.aborted - the process was terminated by a signal
|
||||
// ProcessState.closed - the process resources like opened file descriptors were freed/discarded, final state.
|
||||
pub enum ProcessState {
|
||||
not_started
|
||||
running
|
||||
stopped
|
||||
exited
|
||||
aborted
|
||||
closed
|
||||
}
|
||||
|
||||
// todo(playX): fix reference member access in JS backend
|
||||
// [heap]
|
||||
pub struct Process {
|
||||
pub:
|
||||
filename string
|
||||
pub mut:
|
||||
pid voidptr
|
||||
code int = -1
|
||||
status ProcessState = .not_started
|
||||
// the current status of the process
|
||||
err string // if the process fails, contains the reason why
|
||||
args []string // the arguments that the command takes
|
||||
env_is_custom bool // true, when the environment was customized with .set_environment
|
||||
env []string // the environment with which the process was started (list of 'var=val')
|
||||
use_stdio_ctl bool // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp()
|
||||
use_pgroup bool // when true, the process will create a new process group, enabling .signal_pgkill()
|
||||
stdio_fd [3]int // the stdio file descriptors for the child process, used only by the nix implementation
|
||||
}
|
||||
|
||||
// new_process - create a new process descriptor
|
||||
// NB: new does NOT start the new process.
|
||||
// That is done because you may want to customize it first,
|
||||
// by calling different set_ methods on it.
|
||||
// In order to start it, call p.run() or p.wait()
|
||||
pub fn new_process(filename string) Process {
|
||||
return Process{
|
||||
filename: filename
|
||||
stdio_fd: [-1, -1, -1]!
|
||||
}
|
||||
}
|
||||
|
||||
// set_args - set the arguments for the new process
|
||||
pub fn (mut p Process) set_args(pargs []string) {
|
||||
if p.status != .not_started {
|
||||
return
|
||||
}
|
||||
p.args = pargs
|
||||
return
|
||||
}
|
||||
|
||||
// set_environment - set a custom environment variable mapping for the new process
|
||||
pub fn (mut p Process) set_environment(envs map[string]string) {
|
||||
if p.status != .not_started {
|
||||
return
|
||||
}
|
||||
p.env_is_custom = true
|
||||
p.env = []string{}
|
||||
for k, v in envs {
|
||||
p.env << '$k=$v'
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fn (mut p Process) spawn_internal() {
|
||||
#p.pid = $child_process.spawn(
|
||||
#p.filename+'',
|
||||
#p.args.arr.map((x) => x.valueOf() + ''),
|
||||
#{
|
||||
#env: (p.env_is_custom ? p.env : $process.env),
|
||||
#})
|
||||
#p.pid.on('error', function (err) { builtin.panic('Failed to start subprocess') })
|
||||
|
||||
p.status = .running
|
||||
// todo(playX): stderr,stdin
|
||||
if p.use_stdio_ctl {
|
||||
#p.pid.stdout.pipe(process.stdout)
|
||||
#p.pid.stdin.pipe(process.stdin)
|
||||
#p.pid.stderr.pipe(process.stderr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut p Process) run() {
|
||||
if p.status != .not_started {
|
||||
return
|
||||
}
|
||||
p.spawn_internal()
|
||||
return
|
||||
}
|
||||
|
||||
pub fn (mut p Process) signal_kill() {
|
||||
if p.status !in [.running, .stopped] {
|
||||
return
|
||||
}
|
||||
#p.pid.kill('SIGKILL');
|
||||
|
||||
p.status = .aborted
|
||||
}
|
||||
|
||||
pub fn (mut p Process) signal_stop() {
|
||||
if p.status !in [.running, .stopped] {
|
||||
return
|
||||
}
|
||||
#p.pid.kill('SIGSTOP');
|
||||
|
||||
p.status = .aborted
|
||||
}
|
||||
|
||||
pub fn (mut p Process) signal_continue() {
|
||||
if p.status != .stopped {
|
||||
return
|
||||
}
|
||||
#p.pid.kill('SIGCONT');
|
||||
|
||||
p.status = .running
|
||||
return
|
||||
}
|
||||
|
||||
pub fn (mut p Process) wait() {
|
||||
if p.status == .not_started {
|
||||
p.spawn_internal()
|
||||
}
|
||||
if p.status !in [.running, .stopped] {
|
||||
return
|
||||
}
|
||||
|
||||
p.wait_internal()
|
||||
return
|
||||
}
|
||||
|
||||
fn (mut p Process) wait_internal() {
|
||||
#p.pid.on('exit', function (code) { console.log(code) })
|
||||
}
|
||||
|
||||
pub fn (mut p Process) set_redirect_stdio() {
|
||||
p.use_stdio_ctl = true
|
||||
return
|
||||
}
|
||||
|
||||
pub fn (mut p Process) stdin_write(s string) {
|
||||
p.check_redirection_call('stdin_write')
|
||||
#p.pid.stdin.write(s)
|
||||
}
|
||||
|
||||
// todo(playX): probably does not work
|
||||
|
||||
// will read from stdout pipe, will only return when EOF (end of file) or data
|
||||
// means this will block unless there is data
|
||||
pub fn (mut p Process) stdout_slurp() string {
|
||||
p.check_redirection_call('stdout_slurp')
|
||||
mut res := ''
|
||||
#p.pid.stdout.on('data', function (data) { res = new builtin.string(data) })
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// _check_redirection_call - should be called just by stdxxx methods
|
||||
fn (mut p Process) check_redirection_call(fn_name string) {
|
||||
if !p.use_stdio_ctl {
|
||||
panic('Call p.set_redirect_stdio() before calling p.$fn_name')
|
||||
}
|
||||
if p.status == .not_started {
|
||||
panic('Call p.${fn_name}() after you have called p.run()')
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ const (
|
|||
valid_comp_if_platforms = ['amd64', 'i386', 'aarch64', 'arm64', 'arm32', 'rv64', 'rv32']
|
||||
valid_comp_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian']
|
||||
valid_comp_if_other = ['js', 'debug', 'prod', 'test', 'glibc', 'prealloc',
|
||||
'no_bounds_checking', 'freestanding', 'threads', 'js_browser', 'js_freestanding']
|
||||
'no_bounds_checking', 'freestanding', 'threads', 'js_node', 'js_browser', 'js_freestanding']
|
||||
valid_comp_not_user_defined = all_valid_comptime_idents()
|
||||
array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
|
||||
'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop']
|
||||
|
|
|
@ -126,9 +126,10 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
|||
// builtin types
|
||||
if g.file.mod.name == 'builtin' && !g.generated_builtin {
|
||||
g.gen_builtin_type_defs()
|
||||
g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return this.arr.length;}, set: function(l) { this.arr.length = l; } }); ')
|
||||
g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return this.map.length;}, set: function(l) { this.map.length = l; } }); ')
|
||||
g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return this.arr.length;}, set: function(l) { this.arr.length = l; } }); ')
|
||||
g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new builtin.int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ')
|
||||
g.writeln('Object.defineProperty(string.prototype,"len", { get: function() {return new builtin.int(this.str.length);}, set: function(l) {/* ignore */ } }); ')
|
||||
g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return new builtin.int(this.map.length);}, set: function(l) { this.map.length = l.valueOf(); } }); ')
|
||||
g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new builtin.int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ')
|
||||
g.generated_builtin = true
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue