v.gen.js, os_js: port the OS module to JS (#10872)

pull/10932/head
playX 2021-07-23 18:04:36 +03:00 committed by GitHub
parent 6313ed6a79
commit 69cbdf9fdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 454 additions and 4 deletions

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

95
vlib/os_js/os.js.v 100644
View File

@ -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
}
}

13
vlib/os_js/os.v 100644
View File

@ -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
}

View File

@ -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()')
}
}

View File

@ -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']

View File

@ -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
}