Wasm: implement file IO in WASI

This commit is contained in:
Alexey Andreev 2022-11-07 17:18:32 +01:00
parent 3eb4b742ea
commit e428e2ac7a
12 changed files with 889 additions and 3 deletions

View File

@ -15,7 +15,9 @@
*/ */
package org.teavm.backend.c.intrinsic; package org.teavm.backend.c.intrinsic;
import org.teavm.ast.Expr;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.ast.VariableExpr;
import org.teavm.backend.c.generate.CodeGeneratorUtil; import org.teavm.backend.c.generate.CodeGeneratorUtil;
import org.teavm.backend.c.util.ConstantUtil; import org.teavm.backend.c.util.ConstantUtil;
import org.teavm.interop.Address; import org.teavm.interop.Address;
@ -62,6 +64,8 @@ public class AddressIntrinsic implements Intrinsic {
case "sizeOf": case "sizeOf":
case "ofData": case "ofData":
case "pin":
return true; return true;
default: default:
return false; return false;
@ -214,6 +218,16 @@ public class AddressIntrinsic implements Intrinsic {
+ sizeOf(type.getItemType()) + "))"); + sizeOf(type.getItemType()) + "))");
break; break;
} }
case "pin": {
context.writer().print("/* PIN ");
Expr arg = invocation.getArguments().get(0);
if (arg instanceof VariableExpr) {
context.writer().print(((VariableExpr) arg).getIndex() + " ");
}
context.writer().print("*/");
break;
}
} }
} }

View File

@ -23,6 +23,7 @@ import org.teavm.backend.wasm.generate.WasmClassGenerator;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmDrop;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype; import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
@ -172,6 +173,8 @@ public class AddressIntrinsic implements WasmIntrinsic {
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
manager.generate(invocation.getArguments().get(0)), new WasmInt32Constant(start)); manager.generate(invocation.getArguments().get(0)), new WasmInt32Constant(start));
} }
case "pin":
return new WasmDrop(new WasmInt32Constant(0));
default: default:
throw new IllegalArgumentException(invocation.getMethod().toString()); throw new IllegalArgumentException(invocation.getMethod().toString());
} }

View File

@ -0,0 +1,159 @@
/*
* Copyright 2022 TeaVM Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.runtime.fs;
import static org.teavm.backend.wasm.wasi.Wasi.ERRNO_BADF;
import static org.teavm.backend.wasm.wasi.Wasi.ERRNO_SUCCESS;
import static org.teavm.backend.wasm.wasi.Wasi.PRESTAT_DIR;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.teavm.backend.wasm.wasi.Prestat;
import org.teavm.backend.wasm.wasi.PrestatDir;
import org.teavm.backend.wasm.wasi.Wasi;
import org.teavm.interop.Address;
import org.teavm.runtime.fs.VirtualFile;
import org.teavm.runtime.fs.VirtualFileSystem;
public class WasiFileSystem implements VirtualFileSystem {
private List<Preopened> preopenedList;
final byte[] buffer = new byte[256];
short errno;
String bestPreopenedPath;
int bestPreopenedId;
@Override
public String getUserDir() {
return "/";
}
@Override
public VirtualFile getFile(String path) {
return new WasiVirtualFile(this, path);
}
void findBestPreopened(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
List<Preopened> list = getPreopenedList();
int bestFd = -1;
int bestNameLength = -1;
for (Preopened preopened : list) {
if (path.equals(preopened.name)) {
bestFd = preopened.fd;
path = null;
break;
}
if (preopened.name.length() > bestNameLength) {
int prefixLen = getPrefixLength(path, preopened.name);
if (prefixLen >= 0) {
bestFd = preopened.fd;
bestNameLength = prefixLen;
}
}
}
if (bestFd == -1) {
path = null;
} else if (path != null) {
path = path.substring(bestNameLength);
}
bestPreopenedPath = path;
bestPreopenedId = bestFd;
}
private static int getPrefixLength(String name, String dir) {
if (dir.equals("/")) {
return 0;
} else {
return name.startsWith(dir) && name.length() > dir.length() && name.charAt(dir.length()) == '/'
? dir.length() + 1 : -1;
}
}
@Override
public boolean isWindows() {
return false;
}
@Override
public String canonicalize(String path) {
return path;
}
private List<Preopened> getPreopenedList() {
if (preopenedList == null) {
preopenedList = createPreopenedList();
}
return preopenedList;
}
private List<Preopened> createPreopenedList() {
byte[] buffer = this.buffer;
Prestat prestat = Address.align(Address.ofData(buffer), 4).toStructure();
List<Preopened> list = new ArrayList<>();
int fd = 3; // skip stdin, stdout, and stderr
while (true) {
short errno = Wasi.fdPrestatGet(fd, prestat);
if (errno == ERRNO_BADF) {
break;
}
if (errno != ERRNO_BADF) {
return Collections.emptyList();
}
if (prestat.kind == PRESTAT_DIR) {
PrestatDir prestatDir = (PrestatDir) prestat;
int length = prestatDir.nameLength;
byte[] bytes = length <= buffer.length ? buffer : new byte[length];
errno = Wasi.fdPrestatDirName(fd, Address.ofData(bytes), length);
if (errno == ERRNO_SUCCESS && length > 0) {
if (bytes[length - 1] == 0) {
length -= 1;
}
list.add(new Preopened(fd, new String(bytes, 0, length, StandardCharsets.UTF_8)));
} else {
return Collections.emptyList();
}
}
fd += 1;
}
return list;
}
static class Preopened {
final int fd;
final String name;
Preopened(int fd, String name) {
this.fd = fd;
this.name = name;
}
}
}

View File

@ -0,0 +1,368 @@
/*
* Copyright 2022 TeaVM Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.runtime.fs;
import static org.teavm.backend.wasm.wasi.Wasi.DIRFLAGS_FOLLOW_SYMLINKS;
import static org.teavm.backend.wasm.wasi.Wasi.ERRNO_EXIST;
import static org.teavm.backend.wasm.wasi.Wasi.ERRNO_SUCCESS;
import static org.teavm.backend.wasm.wasi.Wasi.FDFLAGS_APPEND;
import static org.teavm.backend.wasm.wasi.Wasi.FILETYPE_DIRECTORY;
import static org.teavm.backend.wasm.wasi.Wasi.FILETYPE_REGULAR_FILE;
import static org.teavm.backend.wasm.wasi.Wasi.FSTFLAGS_MTIME;
import static org.teavm.backend.wasm.wasi.Wasi.OFLAGS_CREATE;
import static org.teavm.backend.wasm.wasi.Wasi.OFLAGS_DIRECTORY;
import static org.teavm.backend.wasm.wasi.Wasi.OFLAGS_EXCLUSIVE;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_FD_FILESTAT_GET;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_FD_FILESTAT_SET_SIZE;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_READ;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_SEEK;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_SYNC;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_TELL;
import static org.teavm.backend.wasm.wasi.Wasi.RIGHTS_WRITE;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import org.teavm.backend.wasm.runtime.WasiBuffer;
import org.teavm.backend.wasm.wasi.Dirent;
import org.teavm.backend.wasm.wasi.FdResult;
import org.teavm.backend.wasm.wasi.Filestat;
import org.teavm.backend.wasm.wasi.SizeResult;
import org.teavm.backend.wasm.wasi.Wasi;
import org.teavm.interop.Address;
import org.teavm.interop.Structure;
import org.teavm.runtime.fs.VirtualFile;
import org.teavm.runtime.fs.VirtualFileAccessor;
public class WasiVirtualFile implements VirtualFile {
private final WasiFileSystem fs;
private final String fullPath;
private boolean initialized;
private int baseFd;
private String path;
WasiVirtualFile(WasiFileSystem fs, String path) {
this.fs = fs;
fullPath = path;
}
private void init() {
if (initialized) {
return;
}
initialized = true;
fs.findBestPreopened(path);
path = fs.bestPreopenedPath;
baseFd = fs.bestPreopenedId;
fs.bestPreopenedPath = null;
}
@Override
public String getName() {
return path.substring(path.lastIndexOf('/') + 1);
}
Stat stat() {
init();
if (baseFd < 0) {
return null;
}
Filestat filestat = Address.align(WasiBuffer.getBuffer(), 8).toStructure();
int errno;
if (path != null) {
byte[] bytes = path.getBytes(StandardCharsets.UTF_8);
errno = Wasi.pathFilestatGet(baseFd, DIRFLAGS_FOLLOW_SYMLINKS, Address.ofData(bytes), bytes.length,
filestat);
} else {
errno = Wasi.fdFilestatGet(baseFd, filestat);
}
if (errno == ERRNO_SUCCESS) {
return new Stat((byte) filestat.fileType, filestat.lastModified, filestat.size);
} else {
return null;
}
}
@Override
public boolean isDirectory() {
Stat stat = stat();
return stat != null && stat.filetype == FILETYPE_DIRECTORY;
}
@Override
public boolean isFile() {
Stat stat = stat();
return stat != null && stat.filetype == FILETYPE_REGULAR_FILE;
}
private String[] listFiles(int fd) {
SizeResult sizePtr = WasiBuffer.getBuffer().toStructure();
ArrayList<String> list = new ArrayList<>();
final int direntSize = Structure.sizeOf(Dirent.class);
byte[] direntArray = fs.buffer;
Address direntBuffer = Address.align(Address.ofData(direntArray), 8);
int direntArrayOffset = (int) direntBuffer.diff(Address.ofData(direntArray).add(8));
int bufferSize = direntArray.length - direntArrayOffset;
long cookie = 0;
outer:
while (true) {
short errno = Wasi.fdReaddir(fd, direntBuffer, bufferSize, cookie, sizePtr);
if (errno != ERRNO_SUCCESS) {
return null;
}
int remainingSize = bufferSize;
while (true) {
if (remainingSize < direntSize) {
break;
}
Address direntPtr = direntBuffer.add(direntArrayOffset);
Dirent dirent = direntPtr.toStructure();
int entryLength = direntSize + (int) dirent.nameLength;
if (entryLength > bufferSize) {
direntArray = new byte[entryLength * 3 / 2 + 8];
direntBuffer = Address.align(Address.ofData(direntArray), 8);
direntArrayOffset = (int) direntBuffer.diff(Address.ofData(direntArray));
bufferSize = direntArray.length - direntArrayOffset;
break;
}
cookie = dirent.next;
list.add(new String(direntArray, direntArrayOffset + direntSize, (int) dirent.nameLength,
StandardCharsets.UTF_8));
remainingSize -= entryLength;
direntArrayOffset += entryLength;
if (direntArrayOffset == sizePtr.value) {
break outer;
}
}
}
return list.toArray(new String[0]);
}
@Override
public String[] listFiles() {
init();
if (baseFd < 0) {
return null;
}
int fd = path != null ? open(OFLAGS_DIRECTORY, RIGHTS_READ, (short) 0) : baseFd;
if (fd < 0) {
return null;
}
try {
return listFiles(fd);
} finally {
if (path != null) {
close(fd);
}
}
}
private int open(int fd, String path, short oflags, long rights, short fdflags) {
byte[] bytes = path.getBytes(StandardCharsets.UTF_8);
FdResult fdPtr = WasiBuffer.getBuffer().toStructure();
fs.errno = Wasi.pathOpen(fd, DIRFLAGS_FOLLOW_SYMLINKS, Address.ofData(bytes), bytes.length, oflags,
rights, rights, fdflags, fdPtr);
return fs.errno == ERRNO_SUCCESS ? fdPtr.value : -1;
}
private int open(short oflags, long rights, short fdflags) {
return open(baseFd, path, oflags, rights, fdflags);
}
private static void close(int fd) {
// Ignore errno since this is only called in contexts where there's no need to handle the error.
Wasi.fdClose(fd);
}
@Override
public VirtualFileAccessor createAccessor(boolean readable, boolean writable, boolean append) {
init();
if (baseFd < 0) {
return null;
}
long rights = 0;
if (readable) {
rights |= RIGHTS_FD_FILESTAT_GET | RIGHTS_SEEK | RIGHTS_TELL | RIGHTS_READ;
}
if (writable) {
rights |= RIGHTS_FD_FILESTAT_SET_SIZE | RIGHTS_WRITE | RIGHTS_SYNC;
}
short fdflags = (short) 0;
if (append) {
fdflags |= FDFLAGS_APPEND;
}
int fd = open((short) 0, rights, fdflags);
return fd >= 0 ? new WasiVirtualFileAccessor(fs, fd) : null;
}
@Override
public boolean createFile(String fileName) throws IOException {
init();
if (baseFd < 0) {
return false;
}
int fd = open(baseFd, constructPath(path, fileName), (short) (OFLAGS_CREATE | OFLAGS_EXCLUSIVE), 0, (short) 0);
if (fs.errno == ERRNO_EXIST) {
return false;
}
if (fs.errno != ERRNO_SUCCESS) {
throw new IOException("fd_open: " + fs.errno);
}
close(fd);
return true;
}
@Override
public boolean createDirectory(String fileName) {
init();
if (baseFd < 0) {
return false;
}
String filePath = constructPath(path, fileName);
byte[] bytes = filePath.getBytes(StandardCharsets.UTF_8);
return Wasi.pathCreateDirectory(baseFd, Address.ofData(bytes), bytes.length) == ERRNO_SUCCESS;
}
@Override
public boolean delete() {
init();
if (fullPath == null || baseFd < 0) {
return false;
}
byte[] bytes = path.getBytes(StandardCharsets.UTF_8);
short errno = Wasi.pathUnlinkFile(baseFd, Address.ofData(bytes), bytes.length);
return errno == ERRNO_SUCCESS;
}
private static boolean adopt(int fd, WasiVirtualFile file, String fileName) {
byte[] newBytes = fileName.getBytes(StandardCharsets.UTF_8);
byte[] oldBytes = file.path.getBytes(StandardCharsets.UTF_8);
short errno = Wasi.pathRename(file.baseFd, Address.ofData(oldBytes), oldBytes.length, fd,
Address.ofData(newBytes), newBytes.length);
return errno == ERRNO_SUCCESS;
}
@Override
public boolean adopt(VirtualFile file, String fileName) {
if (!(file instanceof WasiVirtualFile)) {
return false;
}
WasiVirtualFile wasiFile = (WasiVirtualFile) file;
wasiFile.init();
if (wasiFile.path == null || fileName == null) {
return false;
}
init();
if (baseFd < 0) {
return false;
}
if (path == null) {
return adopt(baseFd, wasiFile, fileName);
} else {
int fd = open(OFLAGS_DIRECTORY, RIGHTS_READ | RIGHTS_WRITE, (short) 0);
try {
return adopt(fd, wasiFile, fileName);
} finally {
close(fd);
}
}
}
@Override
public boolean canRead() {
init();
int fd = open((short) 0, RIGHTS_READ, (short) 0);
if (fd >= 0) {
close(fd);
return true;
}
return false;
}
@Override
public boolean canWrite() {
init();
int fd = open((short) 0, RIGHTS_WRITE, (short) 0);
if (fd >= 0) {
close(fd);
return true;
}
return false;
}
@Override
public long lastModified() {
Stat stat = stat();
return stat == null ? 0 : stat.mtime / 1000000L;
}
@Override
public boolean setLastModified(long lastModified) {
init();
if (baseFd < 0) {
return false;
}
short errno;
if (path == null) {
errno = Wasi.fdFilestatSetTimes(baseFd, 0, lastModified * 1000000L, FSTFLAGS_MTIME);
} else {
byte[] bytes = path.getBytes(StandardCharsets.UTF_8);
errno = Wasi.pathFilestatSetTimes(baseFd, DIRFLAGS_FOLLOW_SYMLINKS, Address.ofData(bytes), bytes.length,
0, lastModified * 1000000L, FSTFLAGS_MTIME);
}
return errno == ERRNO_SUCCESS;
}
@Override
public boolean setReadOnly(boolean readOnly) {
// TODO: is this possible on WASI?
return false;
}
@Override
public int length() {
Stat stat = stat();
return stat == null ? 0 : (int) stat.filesize;
}
private String constructPath(String parent, String child) {
if (parent == null) {
return child;
}
return !parent.isEmpty() && parent.charAt(parent.length() - 1) == '/'
? parent + child
: parent + '/' + child;
}
static class Stat {
final byte filetype;
final long mtime;
final long filesize;
Stat(byte filetype, long mtime, long filesize) {
this.filetype = filetype;
this.mtime = mtime;
this.filesize = filesize;
}
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright 2022 TeaVM Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.runtime.fs;
import java.io.IOException;
import org.teavm.backend.wasm.runtime.WasiBuffer;
import org.teavm.interop.Address;
import org.teavm.runtime.fs.VirtualFileAccessor;
public class WasiVirtualFileAccessor implements VirtualFileAccessor {
// Enough room for an I32 plus padding for alignment:
private static final byte[] EIGHT_BYTE_BUFFER = new byte[8];
// Enough room for an I64 plus padding for alignment:
private static final byte[] SIXTEEN_BYTE_BUFFER = new byte[16];
private WasiFileSystem fs;
private int fd;
public WasiVirtualFileAccessor(WasiFileSystem fs, int fd) {
this.fs = fs;
this.fd = fd;
}
@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
byte[] vecBuffer = SIXTEEN_BYTE_BUFFER;
Address vec = WasiBuffer.getBuffer();
vec.putInt(Address.ofData(buffer).add(offset).toInt());
vec.add(4).putInt(length);
byte[] sizeBuffer = EIGHT_BYTE_BUFFER;
Address size = Address.align(Address.ofData(sizeBuffer), 4);
short errno = Wasi.fdRead(fd, vec, 1, size);
if (errno == ERRNO_SUCCESS) {
return size.getInt();
} else {
throw new IOException(errnoMessage("fd_read", errno));
}
}
@Override
public void write(byte[] buffer, int offset, int length) throws IOException {
byte[] vecBuffer = SIXTEEN_BYTE_BUFFER;
Address vec = Address.align(Address.ofData(vecBuffer), 4);
byte[] sizeBuffer = EIGHT_BYTE_BUFFER;
Address size = Address.align(Address.ofData(sizeBuffer), 4);
int index = 0;
while (true) {
vec.putInt(Address.ofData(buffer).add(offset + index).toInt());
vec.add(4).putInt(length - index);
short errno = Wasi.fdWrite(fd, vec, 1, size);
if (errno == ERRNO_SUCCESS) {
index += size.getInt();
if (index >= length) {
return;
}
} else {
throw new IOException(errnoMessage("fd_write", errno));
}
}
}
@Override
public int tell() throws IOException {
byte[] filesizeBuffer = SIXTEEN_BYTE_BUFFER;
Address filesize = Address.align(Address.ofData(filesizeBuffer), 8);
short errno = Wasi.fdTell(fd, filesize);
if (errno == ERRNO_SUCCESS) {
return (int) filesize.getLong();
} else {
throw new IOException(errnoMessage("fd_tell", errno));
}
}
private long seek(long offset, byte whence) throws IOException {
byte[] filesizeBuffer = SIXTEEN_BYTE_BUFFER;
Address filesize = Address.align(Address.ofData(filesizeBuffer), 8);
short errno = Wasi.fdSeek(fd, offset, whence, filesize);
if (errno == ERRNO_SUCCESS) {
return filesize.getLong();
} else {
throw new IOException(errnoMessage("fd_seek", errno));
}
}
@Override
public void skip(int amount) throws IOException {
seek(amount, WHENCE_CURRENT);
}
@Override
public void seek(int target) throws IOException {
seek(target, WHENCE_START);
}
@Override
public int size() throws IOException {
return (int) new WasiVirtualFile(fd, null).stat().filesize;
}
@Override
public void resize(int size) throws IOException {
short errno = Wasi.fdFilestatSetSize(fd, size);
if (errno != ERRNO_SUCCESS) {
throw new IOException(errnoMessage("fd_filestat_set_size", errno));
}
}
@Override
public void close() throws IOException {
if (this.fd >= 0) {
int fd = this.fd;
this.fd = -1;
short errno = Wasi.fdClose(fd);
if (errno != ERRNO_SUCCESS) {
throw new IOException(errnoMessage("fd_close", errno));
}
}
}
@Override
public void flush() throws IOException {
short errno = Wasi.fdSync(fd);
if (errno != ERRNO_SUCCESS) {
throw new IOException(errnoMessage("fd_sync", errno));
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Structure;
public class Dirent extends Structure {
public long next;
public long inode;
public long nameLength;
public long fileType;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Structure;
public class FdResult extends Structure {
public int value;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Structure;
public class Filestat extends Structure {
public long deviceId;
public long inode;
public long fileType;
public long linkCount;
public long size;
public long lastAccess;
public long lastModified;
public long lastStatusChange;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Structure;
public class Prestat extends Structure {
public int kind;
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
public class PrestatDir extends Prestat {
public int nameLength;
}

View File

@ -20,14 +20,44 @@ import org.teavm.interop.Import;
public final class Wasi { public final class Wasi {
public static final int CLOCKID_REALTIME = 0; public static final int CLOCKID_REALTIME = 0;
public static final byte PRESTAT_DIR = 0;
public static final short ERRNO_SUCCESS = 0; public static final short ERRNO_SUCCESS = 0;
public static final short ERRNO_BADF = 8;
public static final short ERRNO_EXIST = 20;
public static final short ERRNO_NOENT = 44;
public static final byte FILETYPE_DIRECTORY = 3;
public static final byte FILETYPE_REGULAR_FILE = 4;
public static final int DIRFLAGS_FOLLOW_SYMLINKS = 1;
public static final short OFLAGS_CREATE = 1 << 0;
public static final short OFLAGS_DIRECTORY = 1 << 1;
public static final short OFLAGS_EXCLUSIVE = 1 << 2;
public static final long RIGHTS_READ = 1L << 1;
public static final long RIGHTS_SEEK = 1L << 2;
public static final long RIGHTS_TELL = 1L << 5;
public static final long RIGHTS_WRITE = 1L << 6;
public static final long RIGHTS_SYNC = 1L << 4;
public static final long RIGHTS_CREATE_DIRECTORY = 1L << 9;
public static final long RIGHTS_CREATE_FILE = 1L << 10;
public static final long RIGHTS_FD_FILESTAT_GET = 1L << 21;
public static final long RIGHTS_FD_FILESTAT_SET_SIZE = 1L << 22;
public static final short FDFLAGS_APPEND = 1 << 0;
public static final short FSTFLAGS_MTIME = 1 << 2;
public static final byte WHENCE_START = 0;
public static final byte WHENCE_CURRENT = 1;
public static final byte WHENCE_END = 2;
private Wasi() { private Wasi() {
} }
@Import(name = "fd_write", module = "wasi_snapshot_preview1")
public static native short fdWrite(int fd, IOVec vectors, int vectorsCont, SizeResult result);
@Import(name = "clock_time_get", module = "wasi_snapshot_preview1") @Import(name = "clock_time_get", module = "wasi_snapshot_preview1")
public static native short clockTimeGet(int clockId, long precision, LongResult result); public static native short clockTimeGet(int clockId, long precision, LongResult result);
@ -36,4 +66,47 @@ public final class Wasi {
@Import(name = "args_get", module = "wasi_snapshot_preview1") @Import(name = "args_get", module = "wasi_snapshot_preview1")
public static native short argsGet(Address argv, Address argvBuf); public static native short argsGet(Address argv, Address argvBuf);
@Import(name = "fd_write", module = "wasi_snapshot_preview1")
public static native short fdWrite(int fd, IOVec vectors, int vectorsCont, SizeResult result);
@Import(name = "fd_prestat_get", module = "wasi_snapshot_preview1")
public static native short fdPrestatGet(int fd, Prestat prestat);
@Import(name = "fd_prestat_dir_name", module = "wasi_snapshot_preview1")
public static native short fdPrestatDirName(int fd, Address buffer, int bufferLength);
@Import(name = "fd_filestat_get", module = "wasi_snapshot_preview1")
public static native short fdFilestatGet(int fd, Filestat filestat);
@Import(name = "path_filestat_get", module = "wasi_snapshot_preview1")
public static native short pathFilestatGet(int fd, int lookupFlags, Address path, int pathLength,
Filestat filestat);
@Import(name = "fd_readdir", module = "wasi_snapshot_preview1")
public static native short fdReaddir(int fd, Address dirent, int direntSize, long cookie, SizeResult size);
@Import(name = "path_open", module = "wasi_snapshot_preview1")
public static native short pathOpen(int dirFd, int lookupFlags, Address path, int pathLength, short oflags,
long baseRights, long inheritingRights, short fdflags, FdResult fd);
@Import(name = "fd_close", module = "wasi_snapshot_preview1")
public static native short fdClose(int fd);
@Import(name = "path_create_directory", module = "wasi_snapshot_preview1")
public static native short pathCreateDirectory(int fd, Address path, int pathLength);
@Import(name = "path_unlink_file", module = "wasi_snapshot_preview1")
public static native short pathUnlinkFile(int fd, Address path, int pathLength);
@Import(name = "path_rename", module = "wasi_snapshot_preview1")
public static native short pathRename(int oldFd, Address oldPath, int oldPathLength, int newFd, Address newPath,
int newPathLength);
@Import(name = "fd_filestat_set_times", module = "wasi_snapshot_preview1")
public static native short fdFilestatSetTimes(int fd, long atime, long mtime, short fstflags);
@Import(name = "path_filestat_set_times", module = "wasi_snapshot_preview1")
public static native short pathFilestatSetTimes(int fd, int lookupFlags, Address path, int pathLength,
long atime, long mtime, short fstflags);
} }

View File

@ -93,4 +93,6 @@ public final class Address {
public long diff(Address that) { public long diff(Address that) {
return toLong() - that.toLong(); return toLong() - that.toLong();
} }
public static native void pin(Object obj);
} }