package org.graalvm.wasm.memory;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import static com.oracle.truffle.api.CompilerDirectives.transferToInterpreter;
import static java.lang.Math.toIntExact;
@ExportLibrary(InteropLibrary.class)
public abstract class WasmMemory implements TruffleObject {
static final int PAGE_SIZE = 1 << 16;
static final int LONG_SIZE = 8;
public abstract void copy(Node node, int src, int dst, int n);
public abstract int pageSize();
public abstract int byteSize();
public abstract boolean grow(int extraPageSize);
public boolean growToAddress(int address) {
final int requiredPageCount = address / PAGE_SIZE + 1;
final int extraPageCount = Math.max(0, requiredPageCount - pageSize());
return grow(extraPageCount);
}
public abstract int maxPageSize();
public abstract int load_i32(Node node, int address);
public abstract long load_i64(Node node, int address);
public abstract float load_f32(Node node, int address);
public abstract double load_f64(Node node, int address);
public abstract int load_i32_8s(Node node, int address);
public abstract int load_i32_8u(Node node, int address);
public abstract int load_i32_16s(Node node, int address);
public abstract int load_i32_16u(Node node, int address);
public abstract long load_i64_8s(Node node, int address);
public abstract long load_i64_8u(Node node, int address);
public abstract long load_i64_16s(Node node, int address);
public abstract long load_i64_16u(Node node, int address);
public abstract long load_i64_32s(Node node, int address);
public abstract long load_i64_32u(Node node, int address);
public abstract void store_i32(Node node, int address, int value);
public abstract void store_i64(Node node, int address, long value);
public abstract void store_f32(Node node, int address, float value);
public abstract void store_f64(Node node, int address, double value);
public abstract void store_i32_8(Node node, int address, byte value);
public abstract void store_i32_16(Node node, int address, short value);
public abstract void store_i64_8(Node node, int address, byte value);
public abstract void store_i64_16(Node node, int address, short value);
public abstract void store_i64_32(Node node, int address, int value);
public abstract void clear();
public abstract WasmMemory duplicate();
long[] view(int address, int length) {
long[] chunk = new long[length / 8];
for (int p = address; p < address + length; p += 8) {
chunk[(p - address) / 8] = load_i64(null, p);
}
return chunk;
}
String viewByte(int address) {
final int value = load_i32_8u(null, address);
String result = Integer.toHexString(value);
if (result.length() == 1) {
result = "0" + result;
}
return result;
}
public String hexView(int address, int length) {
long[] chunk = view(address, length);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < chunk.length; i++) {
sb.append("0x").append(hex(address + i * 8)).append(" | ");
for (int j = 0; j < 8; j++) {
sb.append(viewByte(address + i * 8 + j)).append(" ");
}
sb.append("| ");
sb.append(batch(hex(chunk[i]), 2)).append("\n");
}
return sb.toString();
}
private static String hex(long value) {
return pad(Long.toHexString(value), 16);
}
private static String batch(String s, int count) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
result.insert(0, s.charAt(i));
if ((i + 1) % count == 0) {
result.insert(0, " ");
}
}
return result.reverse().toString();
}
private static String pad(String s, int length) {
StringBuilder padded = new StringBuilder(s);
while (padded.length() < length) {
padded.insert(0, "0");
}
return padded.toString();
}
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
long getArraySize() {
return byteSize();
}
@ExportMessage
boolean isArrayElementReadable(long address) {
return address >= 0 && address < getArraySize();
}
@ExportMessage
final boolean isArrayElementModifiable(long address) {
return isArrayElementReadable(address);
}
@SuppressWarnings({"unused", "static-method"})
@ExportMessage
final boolean isArrayElementInsertable(long address) {
return false;
}
@ExportMessage
public Object readArrayElement(long address) throws InvalidArrayIndexException {
if (!isArrayElementReadable(toIntExact(address))) {
transferToInterpreter();
throw InvalidArrayIndexException.create(address);
}
return load_i32_8u(null, toIntExact(address));
}
@ExportMessage(limit = "3")
public void writeArrayElement(long address, Object value, @CachedLibrary("value") InteropLibrary valueLib)
throws InvalidArrayIndexException, UnsupportedMessageException, UnsupportedTypeException {
if (!isArrayElementReadable(toIntExact(address))) {
transferToInterpreter();
throw InvalidArrayIndexException.create(address);
}
byte rawValue;
if (valueLib.fitsInByte(value)) {
rawValue = valueLib.asByte(value);
} else {
throw UnsupportedTypeException.create(new Object[]{value}, "Only bytes can be stored into WebAssembly memory.");
}
store_i32_8(null, toIntExact(address), rawValue);
}
}