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

Enables wasm target in kmem #1623

Merged
merged 1 commit into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions kmem/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
enable.wasm=true
3 changes: 2 additions & 1 deletion kmem/src/commonMain/kotlin/korlibs/memory/Runtime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package korlibs.memory
import korlibs.memory.internal.currentRuntime

enum class Runtime {
JS, JVM, ANDROID, NATIVE;
JS, JVM, ANDROID, NATIVE, WASM;

val isJs: Boolean get() = this == JS
val isJvm: Boolean get() = this == JVM
val isAndroid: Boolean get() = this == ANDROID
val isNative: Boolean get() = this == NATIVE
val isJvmOrAndroid: Boolean get() = isJvm || isAndroid
val isWasm: Boolean get() = this == WASM

companion object {
val CURRENT: Runtime get() = currentRuntime
Expand Down
92 changes: 92 additions & 0 deletions kmem/src/wasmMain/kotlin/korlibs/memory/BufferWasm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package korlibs.memory

import korlibs.memory.internal.*
import org.khronos.webgl.*

actual class Buffer(val dataView: DataView) {
val buffer: ArrayBuffer get() = dataView.buffer

fun sliceUint8Array(offset: Int = 0, size: Int = dataView.byteLength - offset): Uint8Array =
Uint8Array(buffer, dataView.byteOffset + offset, size)
override fun toString(): String = NBuffer_toString(this)
actual companion object {
actual fun copy(src: Buffer, srcPosBytes: Int, dst: Buffer, dstPosBytes: Int, sizeInBytes: Int) {
dst.sliceUint8Array(dstPosBytes, sizeInBytes).set(src.sliceUint8Array(srcPosBytes, sizeInBytes), 0)
}

actual fun equals(src: Buffer, srcPosBytes: Int, dst: Buffer, dstPosBytes: Int, sizeInBytes: Int): Boolean = equalsCommon(src, srcPosBytes, dst, dstPosBytes, sizeInBytes, use64 = false)
}


// @TODO: Optimize by using words instead o bytes
override fun hashCode(): Int {
var h = 1
for (n in 0 until size) h = 31 * h + dataView.getInt8(n)
return h
}

// @TODO: Optimize by using words instead o bytes
override fun equals(other: Any?): Boolean {
if (other !is Buffer || this.size != other.size) return false
val t = this.dataView
val o = other.dataView
for (n in 0 until size) if (t.getInt8(n) != o.getInt8(n)) return false
return true
}
}
actual fun Buffer(size: Int, direct: Boolean): Buffer {
checkNBufferSize(size)
return Buffer(DataView(ArrayBuffer(size)))
}
actual fun Buffer(array: ByteArray, offset: Int, size: Int): Buffer {
checkNBufferWrap(array, offset, size)
return Buffer(DataView(array.unsafeCast<Int8Array>().buffer, offset, size))
}
actual val Buffer.byteOffset: Int get() = this.dataView.byteOffset
actual val Buffer.sizeInBytes: Int get() = this.dataView.byteLength
actual fun Buffer.sliceInternal(start: Int, end: Int): Buffer = Buffer(DataView(this.buffer, this.byteOffset + start, end - start))

// Unaligned versions

actual fun Buffer.getUnalignedInt8(byteOffset: Int): Byte = dataView.getInt8(byteOffset)
actual fun Buffer.getUnalignedInt16(byteOffset: Int): Short = dataView.getInt16(byteOffset, currentIsLittleEndian)
actual fun Buffer.getUnalignedInt32(byteOffset: Int): Int = dataView.getInt32(byteOffset, currentIsLittleEndian)
actual fun Buffer.getUnalignedInt64(byteOffset: Int): Long {
val v0 = getUnalignedInt32(byteOffset).toLong() and 0xFFFFFFFFL
val v1 = getUnalignedInt32(byteOffset + 4).toLong() and 0xFFFFFFFFL
return if (currentIsLittleEndian) (v1 shl 32) or v0 else (v0 shl 32) or v1
}
actual fun Buffer.getUnalignedFloat32(byteOffset: Int): Float = dataView.getFloat32(byteOffset, currentIsLittleEndian)
actual fun Buffer.getUnalignedFloat64(byteOffset: Int): Double = dataView.getFloat64(byteOffset, currentIsLittleEndian)

actual fun Buffer.setUnalignedInt8(byteOffset: Int, value: Byte) = dataView.setInt8(byteOffset, value)
actual fun Buffer.setUnalignedInt16(byteOffset: Int, value: Short) = dataView.setInt16(byteOffset, value, currentIsLittleEndian)
actual fun Buffer.setUnalignedInt32(byteOffset: Int, value: Int) = dataView.setInt32(byteOffset, value, currentIsLittleEndian)
actual fun Buffer.setUnalignedInt64(byteOffset: Int, value: Long) {
setUnalignedInt32(byteOffset, if (currentIsLittleEndian) value.toInt() else (value ushr 32).toInt())
setUnalignedInt32(byteOffset + 4, if (!currentIsLittleEndian) value.toInt() else (value ushr 32).toInt())
}
actual fun Buffer.setUnalignedFloat32(byteOffset: Int, value: Float) = dataView.setFloat32(byteOffset, value, currentIsLittleEndian)
actual fun Buffer.setUnalignedFloat64(byteOffset: Int, value: Double) = dataView.setFloat64(byteOffset, value, currentIsLittleEndian)

fun ArrayBuffer.asUint8ClampedArray(): Uint8ClampedArray = Uint8ClampedArray(this)
fun ArrayBuffer.asUint8Array(): Uint8Array = Uint8Array(this)
fun ArrayBuffer.asInt8Array(): Int8Array = Int8Array(this)
fun ArrayBuffer.asInt16Array(): Int16Array = Int16Array(this)
fun ArrayBuffer.asInt32Array(): Int32Array = Int32Array(this)
fun ArrayBuffer.asFloat32Array(): Float32Array = Float32Array(this)
fun ArrayBuffer.asFloat64Array(): Float64Array = Float64Array(this)

fun ArrayBuffer.asUByteArray(): UByteArray = asUint8Array().unsafeCast<ByteArray>().asUByteArray()
fun ArrayBuffer.asByteArray(): ByteArray = asInt8Array().unsafeCast<ByteArray>()
fun ArrayBuffer.asShortArray(): ShortArray = asInt16Array().unsafeCast<ShortArray>()
fun ArrayBuffer.asIntArray(): IntArray = asInt32Array().unsafeCast<IntArray>()
fun ArrayBuffer.asFloatArray(): FloatArray = asFloat32Array().unsafeCast<FloatArray>()
fun ArrayBuffer.asDoubleArray(): DoubleArray = asFloat64Array().unsafeCast<DoubleArray>()

val Buffer.arrayUByte: Uint8Array get() = Uint8Array(this.buffer, byteOffset, sizeInBytes)
val Buffer.arrayByte: Int8Array get() = Int8Array(buffer, byteOffset, sizeInBytes)
val Buffer.arrayShort: Int16Array get() = Int16Array(buffer, byteOffset, sizeInBytes / 2)
val Buffer.arrayInt: Int32Array get() = Int32Array(buffer, byteOffset, sizeInBytes / 4)
val Buffer.arrayFloat: Float32Array get() = Float32Array(buffer, byteOffset, sizeInBytes / 4)
val Buffer.arrayDouble: Float64Array get() = Float64Array(buffer, byteOffset, sizeInBytes / 8)
66 changes: 66 additions & 0 deletions kmem/src/wasmMain/kotlin/korlibs/memory/FastTransferWasm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package korlibs.memory

@PublishedApi internal val EmptyByteArray = ByteArray(1)
@PublishedApi internal val EmptyShortArray = ShortArray(1)
@PublishedApi internal val EmptyIntArray = IntArray(1)
@PublishedApi internal val EmptyFloatArray = FloatArray(1)

public actual class FastByteTransfer actual constructor() {
@PublishedApi internal var ptr: ByteArray = EmptyByteArray

public actual inline operator fun get(index: Int): Byte = ptr[index]
public actual inline operator fun set(index: Int, value: Byte) { ptr[index] = value }
public actual inline fun use(array: ByteArray, block: (FastByteTransfer) -> Unit) {
try {
ptr = array
block(this)
} finally {
ptr = EmptyByteArray
}
}
}

public actual class FastShortTransfer actual constructor() {
@PublishedApi internal var ptr: ShortArray = EmptyShortArray

public actual inline operator fun get(index: Int): Short = ptr[index]
public actual inline operator fun set(index: Int, value: Short) { ptr[index] = value }
public actual inline fun use(array: ShortArray, block: (FastShortTransfer) -> Unit) {
try {
ptr = array
block(this)
} finally {
ptr = EmptyShortArray
}
}
}

public actual class FastIntTransfer actual constructor() {
@PublishedApi internal var ptr: IntArray = EmptyIntArray

public actual inline operator fun get(index: Int): Int = ptr[index]
public actual inline operator fun set(index: Int, value: Int) { ptr[index] = value }
public actual inline fun use(array: IntArray, block: (FastIntTransfer) -> Unit) {
try {
ptr = array
block(this)
} finally {
ptr = EmptyIntArray
}
}
}

public actual class FastFloatTransfer actual constructor() {
@PublishedApi internal var ptr: FloatArray = EmptyFloatArray

public actual inline operator fun get(index: Int): Float = ptr[index]
public actual inline operator fun set(index: Int, value: Float) { ptr[index] = value }
public actual inline fun use(array: FloatArray, block: (FastFloatTransfer) -> Unit) {
try {
ptr = array
block(this)
} finally {
ptr = EmptyFloatArray
}
}
}
4 changes: 4 additions & 0 deletions kmem/src/wasmMain/kotlin/korlibs/memory/KmemGCWasm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package korlibs.memory

public actual val KmemGC: KmemGCImpl = object : KmemGCImpl() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package korlibs.memory.atomic

actual class KmemAtomicRef<T> actual constructor(initial: T) {
actual var value: T = initial
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package korlibs.memory.dyn

public actual open class DynamicLibraryBase actual constructor(names: List<String>) : DynamicSymbolResolver {
actual val isAvailable: Boolean get() = false
override fun getSymbol(name: String): KPointer? = TODO()
actual fun close() {
}
}
27 changes: 27 additions & 0 deletions kmem/src/wasmMain/kotlin/korlibs/memory/dyn/DynamicLibraryJs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package korlibs.memory.dyn

import kotlin.reflect.*

actual inline fun <reified T : Function<*>> DynamicLibrary.func(name: String?): DynamicFunLibraryNotNull<T> = DynamicFun<T>(this, name, T::class, typeOf<T>())
//public fun <T : Function<*>> DynamicLibrary.funcNull(name: String? = null): DynamicFunLibraryNull<T> = DynamicFunLibraryNull<T>(this, name)

//actual inline fun <reified T : Function<*>> DynamicLibrary.sfunc(name: String? = null): DynamicFunLibrary<T>
//actual inline fun <reified T : Function<*>> DynamicLibrary.sfuncNull(name: String? = null): DynamicFunLibraryNull<T>


@OptIn(ExperimentalStdlibApi::class)
public open class DynamicFun<T : Function<*>>(
library: DynamicSymbolResolver,
name: String? = null,
val clazz: KClass<T>,
val funcType: KType
) : DynamicFunLibraryNotNull<T>(library, name) {
override fun getValue(obj: Any?, property: KProperty<*>): KPointerTT<KFunctionTT<T>> {
// @TODO: We can call the global scope maybe?
//return { } as T
return KPointerTT(byteArrayOf())
}
}

//actual inline operator fun <R> KPointerTT<KFunctionTT<() -> R>>.invoke(): R = TODO()
//actual inline operator fun <P1, R> KPointerTT<KFunctionTT<(P1) -> R>>.invoke(p1: P1): R = TODO()
35 changes: 35 additions & 0 deletions kmem/src/wasmMain/kotlin/korlibs/memory/dyn/KStructureImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package korlibs.memory.dyn

import korlibs.memory.*

actual class KArena actual constructor() {
actual fun allocBytes(size: Int): KPointer = KPointer(ByteArray(size))
actual fun clear(): Unit = Unit
}

actual val POINTER_SIZE: Int = 4
actual val LONG_SIZE: Int = 8

actual abstract class KPointed
actual class KPointerTT<T : KPointed>(val ptr: ByteArray)
fun <T : KPointed> KPointer(ptr: ByteArray): KPointerTT<T> = KPointerTT<T>(ptr)
actual class KFunctionTT<T : Function<*>>(val func: T) : KPointed()

actual abstract class KStructureBase {
actual abstract val pointer: KPointer?
}
actual fun KPointer(address: Long): KPointer = TODO()
actual val KPointer.address: Long get() = TODO()

actual fun KPointer.getByte(offset: Int): Byte = this.ptr.readS8(offset).toByte()
actual fun KPointer.setByte(offset: Int, value: Byte): Unit = this.ptr.write8(offset, value.toInt())
actual fun KPointer.getShort(offset: Int): Short = this.ptr.readS16LE(offset).toShort()
actual fun KPointer.setShort(offset: Int, value: Short): Unit = this.ptr.write16LE(offset, value.toInt())
actual fun KPointer.getInt(offset: Int): Int = this.ptr.readS32LE(offset)
actual fun KPointer.setInt(offset: Int, value: Int): Unit = this.ptr.write32LE(offset, value)
actual fun KPointer.getFloat(offset: Int): Float = this.ptr.readF32LE(offset)
actual fun KPointer.setFloat(offset: Int, value: Float): Unit = this.ptr.writeF32LE(offset, value)
actual fun KPointer.getDouble(offset: Int): Double = this.ptr.readF64LE(offset)
actual fun KPointer.setDouble(offset: Int, value: Double): Unit = this.ptr.writeF64LE(offset, value)
actual fun KPointer.getLong(offset: Int): Long = this.ptr.readS64LE(offset)
actual fun KPointer.setLong(offset: Int, value: Long): Unit = this.ptr.write64LE(offset, value)
51 changes: 51 additions & 0 deletions kmem/src/wasmMain/kotlin/korlibs/memory/internal/currentWasm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package korlibs.memory.internal

import korlibs.memory.Arch
import korlibs.memory.Os
import korlibs.memory.Runtime
import org.khronos.webgl.Uint32Array
import org.khronos.webgl.Uint8Array
import org.khronos.webgl.get

@JsFun("() => { return (typeof Deno === 'object' && Deno.statSync) }")
internal external fun isDenoJs(): Boolean
@JsFun("() => { return (typeof window === 'object') }")
internal external fun isWeb(): Boolean
@JsFun("() => { return (typeof importScripts === 'function') }")
internal external fun isWorker(): Boolean
@JsFun("() => { return ((typeof process !== 'undefined') && process.release && (process.release.name.search(/node|io.js/) !== -1)) }")
internal external fun isNodeJs(): Boolean
internal fun isShell(): Boolean = !isWeb() && !isNodeJs() && !isWorker()

// @TODO: Check navigator.userAgent
internal actual val currentOs: Os = Os.UNKNOWN
internal actual val currentArch: Arch = Arch.UNKNOWN

internal actual val currentRuntime: Runtime = Runtime.WASM
internal actual val currentIsDebug: Boolean = false
internal actual val currentIsLittleEndian: Boolean = Uint8Array(Uint32Array(arrayOf(0x11223344)).buffer)[0].toInt() == 0x44
internal actual val multithreadedSharedHeap: Boolean = false // Workers have different heaps

internal actual val currentRawPlatformName: String = when {
isDenoJs() -> "wasm-deno"
isWeb() -> "wasm-web"
isNodeJs() -> "wasm-node"
isWorker() -> "wasm-worker"
isShell() -> "wasm-shell"
else -> "wasm"
}

private external class Navigator {
val userAgent: String
}
private external class Process {
val platform: String
}
private external val navigator: Navigator // browser
private external val process: Process // nodejs

internal actual val currentRawOsName: String = when {
isDenoJs() -> "deno"
isWeb() || isWorker() -> navigator.userAgent
else -> process.platform
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package korlibs.memory.internal

@JsFun("(obj) => { return obj }")
private external fun <T> __unsafeCast(value: Any?): T

fun <T> Any?.unsafeCast(): T = __unsafeCast(this)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package kotlinx.cinterop

/*
import korlibs.memory.dyn.*

actual operator fun <R> KPointerTT<KFunctionTT<() -> R>>.invoke(): R = TODO()
actual operator fun <P1, R> KPointerTT<KFunctionTT<(P1) -> R>>.invoke(p1: P1): R = TODO()
actual operator fun <P1, P2, R> KPointerTT<KFunctionTT<(P1, P2) -> R>>.invoke(p1: P1, p2: P2): R = TODO()
actual operator fun <P1, P2, P3, R> KPointerTT<KFunctionTT<(P1, P2, P3) -> R>>.invoke(p1: P1, p2: P2, p3: P3): R = TODO()
actual operator fun <P1, P2, P3, P4, R> KPointerTT<KFunctionTT<(P1, P2, P3, P4) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4): R = TODO()
actual operator fun <P1, P2, P3, P4, P5, R> KPointerTT<KFunctionTT<(P1, P2, P3, P4, P5) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5): R = TODO()
actual operator fun <P1, P2, P3, P4, P5, P6, R> KPointerTT<KFunctionTT<(P1, P2, P3, P4, P5, P6) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6): R = TODO()
actual operator fun <P1, P2, P3, P4, P5, P6, P7, R> KPointerTT<KFunctionTT<(P1, P2, P3, P4, P5, P6, P7) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7): R = TODO()
*/