Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(asys) API #8832

Open
wants to merge 7 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src-json/define.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
"define": "as3",
"doc": "Defined when outputting flash9 as3 source code."
},
{
"name": "Asys",
"define": "asys",
"doc": "Defined for all platforms that support the libuv-based asys package."
},
{
"name": "CheckXmlProxy",
"define": "check_xml_proxy",
Expand Down
8 changes: 8 additions & 0 deletions src/context/common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ type platform_config = {
pf_static : bool;
(** has access to the "sys" package *)
pf_sys : bool;
(** has access to the "asys" package *)
pf_asys : bool;
(** captured variables handling (see before) *)
pf_capture_policy : capture_policy;
(** when calling a method with optional args, do we replace the missing args with "null" constants *)
Expand Down Expand Up @@ -312,6 +314,7 @@ let default_config =
{
pf_static = true;
pf_sys = true;
pf_asys = false;
pf_capture_policy = CPNone;
pf_pad_nulls = false;
pf_add_final_return = false;
Expand Down Expand Up @@ -588,6 +591,11 @@ let init_platform com pf =
define com Define.Sys
end else
com.package_rules <- PMap.add "sys" Forbidden com.package_rules;
if com.config.pf_asys then begin
raw_define_value com.defines "target.asys" "true";
define com Define.Asys
end else
com.package_rules <- PMap.add "asys" Forbidden com.package_rules;
if com.config.pf_uses_utf16 then begin
raw_define_value com.defines "target.utf16" "true";
define com Define.Utf16;
Expand Down
52 changes: 52 additions & 0 deletions std/asys/AsyncFileSystem.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package asys;

import haxe.Error;
import haxe.NoData;
import haxe.async.Callback;
import haxe.io.Bytes;
import haxe.io.FilePath;
import asys.*;

/**
This class provides methods for asynchronous operations on files and
directories. For synchronous operations, see `asys.FileSystem`.

All methods here are asynchronous versions of the functions in
`asys.FileSystem`. Please see them for a description of the arguments and
use of each method.

Any synchronous method that returns no value (`Void` return type) has an
extra `callback:Callback<NoData>` argument.

Any synchronous method that returns a value has an extra
`callback:Callback<T>` argument, where `T` is the return type of the
synchronous method.

Errors are communicated through the callbacks or in some cases thrown
immediately.
**/
extern class AsyncFileSystem {
static function access(path:FilePath, ?mode:FileAccessMode = FileAccessMode.Ok, callback:Callback<NoData>):Void;
static function chmod(path:FilePath, mode:FilePermissions, ?followSymLinks:Bool = true, callback:Callback<NoData>):Void;
static function chown(path:FilePath, uid:Int, gid:Int, ?followSymLinks:Bool = true, callback:Callback<NoData>):Void;
static function copyFile(src:FilePath, dest:FilePath, ?flags:FileCopyFlags, callback:Callback<NoData>):Void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flags of asys.FileSystem.access() is commented out

static function exists(path:FilePath, callback:Callback<Bool>):Void;
static function link(existingPath:FilePath, newPath:FilePath, callback:Callback<NoData>):Void;
static function mkdir(path:FilePath, ?recursive:Bool, ?mode:FilePermissions, callback:Callback<NoData>):Void;
static function mkdtemp(prefix:FilePath, callback:Callback<FilePath>):Void;
static function readdir(path:FilePath, callback:Callback<Array<FilePath>>):Void;
static function readdirTypes(path:FilePath, callback:Callback<Array<DirectoryEntry>>):Void;
static function readlink(path:FilePath, callback:Callback<FilePath>):Void;
static function realpath(path:FilePath, callback:Callback<FilePath>):Void;
static function rename(oldPath:FilePath, newPath:FilePath, callback:Callback<NoData>):Void;
static function rmdir(path:FilePath, callback:Callback<NoData>):Void;
static function stat(path:FilePath, ?followSymLinks:Bool = true, callback:Callback<FileStat>):Void;
static function symlink(target:FilePath, path:FilePath, ?type:String, callback:Callback<NoData>):Void;
static function truncate(path:FilePath, len:Int, callback:Callback<NoData>):Void;
static function unlink(path:FilePath, callback:Callback<NoData>):Void;
static function utimes(path:FilePath, atime:Date, mtime:Date, callback:Callback<NoData>):Void;
static function appendFile(path:FilePath, data:Bytes, ?flags:FileOpenFlags, ?mode:FilePermissions, callback:Callback<NoData>):Void;
static function open(path:FilePath, ?flags:FileOpenFlags, ?mode:FilePermissions, ?binary:Bool = true, callback:Callback<sys.io.File>):Void;
static function readFile(path:FilePath, ?flags:FileOpenFlags, callback:Callback<Bytes>):Void;
static function writeFile(path:FilePath, data:Bytes, ?flags:FileOpenFlags, ?mode:FilePermissions, callback:Callback<NoData>):Void;
}
54 changes: 54 additions & 0 deletions std/asys/CurrentProcess.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package asys;

import haxe.async.*;
import asys.net.Socket;
import asys.io.*;

/**
Methods to control the current process and IPC interaction with the parent
process.
**/
class CurrentProcess {
/**
Emitted when a message is received over IPC. `initIpc` must be called first
to initialise the IPC channel.
**/
public static final messageSignal:Signal<IpcMessage> = new ArraySignal();

static var ipc:Socket;
static var ipcOut:IpcSerializer;
static var ipcIn:IpcUnserializer;

/**
Initialise the IPC channel on the given file descriptor `fd`. This should
only be used when the current process was spawned with `Process.spawn` from
another Haxe process. `fd` should correspond to the index of the `Ipc`
entry in `options.stdio`.
**/
public static function initIpc(fd:Int):Void {
if (ipc != null)
throw "IPC already initialised";
ipc = Socket.create();
ipcOut = @:privateAccess new IpcSerializer(ipc);
ipcIn = @:privateAccess new IpcUnserializer(ipc);
ipc.connectFd(true, fd);
ipc.errorSignal.on(err -> trace("IPC error", err));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this error should be exposed somehow instead of just tracing it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, CurrentProcess is one of the later additions, this was just a debug trace.

ipcIn.messageSignal.on(message -> messageSignal.emit(message));
}

/**
Sends a message over IPC. `initIpc` must be called first to initialise the
IPC channel.
**/
public static function send(message:IpcMessage):Void {
if (ipc == null)
throw "IPC not connected";
ipcOut.write(message);
}

extern public static function initUv():Void;

extern public static function runUv(?mode:asys.uv.UVRunMode = RunDefault):Bool;

extern public static function stopUv():Void;
}
17 changes: 17 additions & 0 deletions std/asys/DirectoryEntry.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package asys;

import haxe.io.FilePath;

/**
An entry returned from `asys.FileSystem.readdirTypes`.
**/
interface DirectoryEntry {
var name(get, never):FilePath;
function isBlockDevice():Bool;
function isCharacterDevice():Bool;
function isDirectory():Bool;
function isFIFO():Bool;
function isFile():Bool;
function isSocket():Bool;
function isSymbolicLink():Bool;
}
16 changes: 16 additions & 0 deletions std/asys/FileAccessMode.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package asys;

/**
Wrapper for file access modes. See `asys.FileSystem.access`.
**/
enum abstract FileAccessMode(Int) {
var Ok = 0;
var Execute = 1 << 0;
var Write = 1 << 1;
var Read = 1 << 2;

inline function get_raw():Int return this;

@:op(A | B)
inline function join(other:FileAccessMode) return this | other.get_raw();
Aurel300 marked this conversation as resolved.
Show resolved Hide resolved
}
23 changes: 23 additions & 0 deletions std/asys/FileCopyFlags.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package asys;

enum abstract FileCopyFlags(Int) {
/**
Fail if destination exists.
**/
var FailIfExists = 1 << 0;

/**
Copy-on-write reflink if possible.
**/
var COWClone = 1 << 1;

/**
Copy-on-write reflink or fail.
**/
var COWCloneForce = 1 << 2;

inline function get_raw():Int return this;

@:op(A | B)
inline function join(other:FileCopyFlags) return this | other.get_raw();
}
80 changes: 80 additions & 0 deletions std/asys/FileOpenFlags.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package asys;

class FileOpenFlagsImpl {
public static function fromString(flags:String):FileOpenFlags {
return (switch (flags) {
case "r": ReadOnly;
case "r+": ReadWrite;
case "rs+": ReadWrite | Sync;
case "sr+": ReadWrite | Sync;
case "w": Truncate | Create | WriteOnly;
case "w+": Truncate | Create | ReadWrite;
case "a": Append | Create | WriteOnly;
case "a+": Append | Create | ReadWrite;
case "wx": Truncate | Create | WriteOnly | Excl;
case "xw": Truncate | Create | WriteOnly | Excl;
case "wx+": Truncate | Create | ReadWrite | Excl;
case "xw+": Truncate | Create | ReadWrite | Excl;
case "ax": Append | Create | WriteOnly | Excl;
case "xa": Append | Create | WriteOnly | Excl;
case "as": Append | Create | WriteOnly | Sync;
case "sa": Append | Create | WriteOnly | Sync;
case "ax+": Append | Create | ReadWrite | Excl;
case "xa+": Append | Create | ReadWrite | Excl;
case "as+": Append | Create | ReadWrite | Sync;
case "sa+": Append | Create | ReadWrite | Sync;
case _: throw "invalid file open flags";
});
}
}

/**
Flags used when opening a file with `asys.FileSystem.open` or other file
functions. Specify whether the opened file:

- will be readable
- will be writable
- will be truncated (all data lost) first
- will be in append mode
- will be opened exclusively by this process

Instances of this type can be created by combining flags with the bitwise or
operator:

```haxe
Truncate | Create | WriteOnly
```

Well-known combinations of flags can be specified with a string. The
supported modes are: `r`, `r+`, `rs+`, `sr+`, `w`, `w+`, `a`, `a+`, `wx`,
`xw`, `wx+`, `xw+`, `ax`, `xa`, `as`, `sa`, `ax+`, `xa+`, `as+`, `sa+`.
**/
@:native("asys.FileOpenFlagsImpl")
extern enum abstract FileOpenFlags(Int) {
@:from public static function fromString(flags:String):FileOpenFlags;

inline function new(value:Int)
this = value;

inline function get_raw():Int return this;

@:op(A | B)
inline function join(other:FileOpenFlags):FileOpenFlags return new FileOpenFlags(this | other.get_raw());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spoils our enums behavior. It makes switches non-exhaustive, and compiler is not aware of that.


// TODO: some of these don't make sense in Haxe-wrapped libuv
var Append;
var Create;
var Direct;
var Directory;
var Dsync;
var Excl;
var NoAtime;
var NoCtty;
var NoFollow;
var NonBlock;
var ReadOnly;
var ReadWrite;
var Sync;
var Truncate;
var WriteOnly;
}
90 changes: 90 additions & 0 deletions std/asys/FilePermissions.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package asys;

/**
File permissions in specify whether a file can be read, written, or executed
by its owner, its owning group, and everyone else. Instances of this type
can be constructed by combining individual file permissions with the `|`
operator:

```haxe
ReadOwner | WriteOwner | ReadGroup | ReadOthers
```

Alternatively, file permissions may be specified as a string with exactly 9
characters, in the format `rwxrwxrwx`, where each letter may instead be a
`-` character. The first three characters represent the permissions of the
owner, the second three characters represent the permissions of the owning
group, and the last three characters represent the permissions of everyone
else.

```haxe
"rw-r--r--"
```

Finally, file permissions may be constructed from an octal representation
using the `fromOctal` function.

```haxe
FilePermissions.fromOctal("644")
```
**/
enum abstract FilePermissions(Int) {
@:from public static function fromString(s:String):FilePermissions {
inline function bit(cc:Int, expect:Int):Int {
return (if (cc == expect)
1;
else if (cc == "-".code)
0;
else
throw "invalid file permissions string");
}
switch (s.length) {
case 9: // rwxrwxrwx
return new FilePermissions(bit(s.charCodeAt(0), "r".code) << 8
| bit(s.charCodeAt(1), "w".code) << 7
| bit(s.charCodeAt(2), "x".code) << 6
| bit(s.charCodeAt(3), "r".code) << 5
| bit(s.charCodeAt(4), "w".code) << 4
| bit(s.charCodeAt(5), "x".code) << 3
| bit(s.charCodeAt(6), "r".code) << 2
| bit(s.charCodeAt(7), "w".code) << 1
| bit(s.charCodeAt(8), "x".code));
case _:
throw "invalid file permissions string";
}
}

public static function fromOctal(s:String):FilePermissions {
inline function digit(n:Int):Int {
if (n >= "0".code && n <= "7".code) return n - "0".code;
throw "invalid octal file permissions";
}
switch (s.length) {
case 3: // 777
return new FilePermissions(digit(s.charCodeAt(0)) << 6
| digit(s.charCodeAt(1)) << 3
| digit(s.charCodeAt(2)));
case _:
throw "invalid octal file permissions";
}
}

var None = 0;
var ExecuteOthers = 1 << 0;
var WriteOthers = 1 << 1;
var ReadOthers = 1 << 2;
var ExecuteGroup = 1 << 3;
var WriteGroup = 1 << 4;
var ReadGroup = 1 << 5;
var ExecuteOwner = 1 << 6;
var WriteOwner = 1 << 7;
var ReadOwner = 1 << 8;

inline function new(value:Int)
this = value;

inline function get_raw():Int return this;

@:op(A | B)
inline function join(other:FilePermissions) return new FilePermissions(this | other.get_raw());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This subverts enums. We should have FilePermissionsSet(Int) from FilePermission abstract and use it as an argument type for corresponding methods.

}
Loading