Kotlin Standard Library implementation for WebAssembly System Interface (WASI) platform providing essential I/O, time, random, UUID, and reflection capabilities.
—
The Exception Handling capabilities provide WASI-specific implementations for Kotlin's exception system. The implementation includes the base Throwable class with limited stack trace support and comprehensive error code mapping for WASI-specific system call failures.
/**
* The base class for all errors and exceptions in WASI environment.
* Provides limited stack trace support due to WebAssembly constraints.
*/
actual class Throwable {
/**
* Creates a throwable with no message and no cause.
*/
actual constructor()
/**
* Creates a throwable with the specified message.
* @param message the detail message
*/
actual constructor(message: String?)
/**
* Creates a throwable with the specified cause.
* @param cause the cause of this throwable
*/
actual constructor(cause: Throwable?)
/**
* Creates a throwable with the specified message and cause.
* @param message the detail message
* @param cause the cause of this throwable
*/
actual constructor(message: String?, cause: Throwable?)
/**
* The detail message of this throwable.
*/
actual val message: String?
/**
* The cause of this throwable, or null if the cause is unknown.
*/
actual val cause: Throwable?
}/**
* Internal WASI error codes from preview1 specification.
* Used for mapping WASI system call errors to Kotlin exceptions.
*/
internal enum class WasiErrorCode {
SUCCESS, // No error occurred
E2BIG, // Argument list too long
EACCES, // Permission denied
EADDRINUSE, // Address already in use
EADDRNOTAVAIL, // Address not available
EAFNOSUPPORT, // Address family not supported
EAGAIN, // Resource temporarily unavailable
EALREADY, // Connection already in progress
EBADF, // Bad file descriptor
EBADMSG, // Bad message
EBUSY, // Device or resource busy
ECANCELED, // Operation canceled
ECHILD, // No child processes
ECONNABORTED, // Connection aborted
ECONNREFUSED, // Connection refused
ECONNRESET, // Connection reset
EDEADLK, // Resource deadlock avoided
EDESTADDRREQ, // Destination address required
EDOM, // Mathematics argument out of domain
EDQUOT, // Disk quota exceeded
EEXIST, // File exists
EFAULT, // Bad address
EFBIG, // File too large
EHOSTUNREACH, // Host is unreachable
EIDRM, // Identifier removed
EILSEQ, // Invalid or incomplete multibyte sequence
EINPROGRESS, // Operation in progress
EINTR, // Interrupted system call
EINVAL, // Invalid argument
EIO, // Input/output error
EISCONN, // Socket is connected
EISDIR, // Is a directory
ELOOP, // Symbolic link loop
EMFILE, // Too many open files
EMLINK, // Too many links
EMSGSIZE, // Message too large
EMULTIHOP, // Multihop attempted
ENAMETOOLONG, // File name too long
ENETDOWN, // Network is down
ENETRESET, // Network dropped connection on reset
ENETUNREACH, // Network is unreachable
ENFILE, // Too many open files in system
ENOBUFS, // No buffer space available
ENODEV, // No such device
ENOENT, // No such file or directory
ENOEXEC, // Executable file format error
ENOLCK, // No locks available
ENOLINK, // Link has been severed
ENOMEM, // Cannot allocate memory
ENOMSG, // No message of desired type
ENOPROTOOPT, // Protocol not available
ENOSPC, // No space left on device
ENOSYS, // Function not implemented
ENOTCONN, // Socket is not connected
ENOTDIR, // Not a directory
ENOTEMPTY, // Directory not empty
ENOTRECOVERABLE, // State not recoverable
ENOTSOCK, // Not a socket
ENOTSUP, // Operation not supported
ENOTTY, // Inappropriate I/O control operation
ENXIO, // No such device or address
EOVERFLOW, // Value too large for data type
EOWNERDEAD, // Previous owner died
EPERM, // Operation not permitted
EPIPE, // Broken pipe
EPROTO, // Protocol error
EPROTONOSUPPORT, // Protocol not supported
EPROTOTYPE, // Protocol wrong type for socket
ERANGE, // Result too large
EROFS, // Read-only file system
ESPIPE, // Invalid seek
ESRCH, // No such process
ESTALE, // Stale file handle
ETIMEDOUT, // Connection timed out
ETXTBSY, // Text file busy
EXDEV, // Invalid cross-device link
ENOTCAPABLE // Capability insufficient
}
/**
* Internal exception class for WASI-specific errors.
* Maps WASI error codes to Kotlin exceptions with appropriate messages.
*/
internal class WasiError(
val errorCode: WasiErrorCode,
message: String = errorCode.name
) : Exception(message)// Throwing and catching exceptions
fun riskyOperation() {
throw RuntimeException("Something went wrong")
}
try {
riskyOperation()
} catch (e: RuntimeException) {
println("Caught exception: ${e.message}")
println("Cause: ${e.cause}")
}// Create custom exception types
class ConfigurationException(
message: String,
cause: Throwable? = null
) : Exception(message, cause)
class ValidationException(
val field: String,
message: String
) : Exception("Validation failed for field '$field': $message")
// Usage
fun validateUser(user: User) {
if (user.name.isBlank()) {
throw ValidationException("name", "Name cannot be blank")
}
if (user.email.contains("@").not()) {
throw ValidationException("email", "Invalid email format")
}
}
try {
validateUser(User("", "invalid-email"))
} catch (e: ValidationException) {
println("Validation error in ${e.field}: ${e.message}")
}// Chain exceptions to preserve error context
class DatabaseService {
fun saveUser(user: User) {
try {
// Simulate database operation
performDatabaseOperation(user)
} catch (e: Exception) {
throw RuntimeException("Failed to save user: ${user.name}", e)
}
}
private fun performDatabaseOperation(user: User) {
// Simulate a lower-level exception
throw IllegalStateException("Database connection lost")
}
}
// Usage
val service = DatabaseService()
try {
service.saveUser(User("Alice", "alice@example.com"))
} catch (e: RuntimeException) {
println("Top-level error: ${e.message}")
println("Root cause: ${e.cause?.message}")
// Walk the exception chain
var current: Throwable? = e
var depth = 0
while (current != null) {
println(" ${" ".repeat(depth)}${current::class.simpleName}: ${current.message}")
current = current.cause
depth++
}
}// Safe resource management with exception handling
class ResourceManager : AutoCloseable {
private var closed = false
fun performOperation() {
if (closed) {
throw IllegalStateException("Resource is closed")
}
// Simulate operation that might fail
if (Random.nextBoolean()) {
throw RuntimeException("Operation failed randomly")
}
println("Operation completed successfully")
}
override fun close() {
if (!closed) {
closed = true
println("Resource closed")
}
}
}
// Use with try-with-resources pattern
fun useResource() {
try {
ResourceManager().use { resource ->
resource.performOperation()
resource.performOperation()
}
} catch (e: Exception) {
println("Operation failed: ${e.message}")
}
}// Retry with exponential backoff
suspend fun retryWithBackoff(
maxRetries: Int = 3,
baseDelayMs: Long = 1000,
operation: suspend () -> Unit
) {
var lastException: Exception? = null
repeat(maxRetries) { attempt ->
try {
operation()
return // Success
} catch (e: Exception) {
lastException = e
if (attempt < maxRetries - 1) {
val delay = baseDelayMs * (1L shl attempt) // Exponential backoff
println("Attempt ${attempt + 1} failed, retrying in ${delay}ms: ${e.message}")
kotlinx.coroutines.delay(delay)
}
}
}
// All retries failed
throw RuntimeException("Operation failed after $maxRetries attempts", lastException)
}
// Circuit breaker pattern
class CircuitBreaker(
private val failureThreshold: Int = 5,
private val recoveryTimeMs: Long = 60000
) {
private var failures = 0
private var lastFailureTime = 0L
private var state = State.CLOSED
enum class State { CLOSED, OPEN, HALF_OPEN }
suspend fun <T> execute(operation: suspend () -> T): T {
when (state) {
State.OPEN -> {
if (System.currentTimeMillis() - lastFailureTime > recoveryTimeMs) {
state = State.HALF_OPEN
} else {
throw RuntimeException("Circuit breaker is OPEN")
}
}
State.HALF_OPEN -> {
// Allow one test call
}
State.CLOSED -> {
// Normal operation
}
}
try {
val result = operation()
// Success - reset circuit breaker
failures = 0
state = State.CLOSED
return result
} catch (e: Exception) {
failures++
lastFailureTime = System.currentTimeMillis()
if (failures >= failureThreshold) {
state = State.OPEN
}
throw e
}
}
}// Handle WASI-specific errors (internal implementation detail)
internal fun handleWasiError(errorCode: Int, operation: String) {
if (errorCode != 0) {
val wasiError = WasiErrorCode.values().getOrNull(errorCode)
val message = when (wasiError) {
WasiErrorCode.EBADF -> "Bad file descriptor in $operation"
WasiErrorCode.EIO -> "I/O error during $operation"
WasiErrorCode.ENOSPC -> "No space left on device during $operation"
WasiErrorCode.EPIPE -> "Broken pipe during $operation"
WasiErrorCode.EFAULT -> "Bad address during $operation"
WasiErrorCode.EINVAL -> "Invalid argument in $operation"
WasiErrorCode.ENOSYS -> "Function not implemented: $operation"
else -> "WASI error $errorCode during $operation"
}
throw WasiError(wasiError ?: WasiErrorCode.SUCCESS, message)
}
}
// Example usage in I/O operations
fun safeWriteToConsole(message: String) {
try {
println(message)
} catch (e: WasiError) {
when (e.errorCode) {
WasiErrorCode.EPIPE -> {
// Output stream closed, log to alternative location
System.err.println("Console output unavailable: ${e.message}")
}
WasiErrorCode.EIO -> {
// I/O error, retry once
try {
Thread.sleep(100)
println(message)
} catch (retryException: Exception) {
System.err.println("Failed to write after retry: ${retryException.message}")
}
}
else -> {
System.err.println("Unexpected WASI error: ${e.message}")
}
}
}
}Due to WebAssembly constraints, stack traces in WASI have limitations:
// Exceptions are designed for minimal memory overhead
val exception = RuntimeException("Error message")
// Only stores message and cause references, no heavyweight stack trace dataWASI system call errors are comprehensively mapped:
// Exceptions are lightweight in WASI
val quickException = RuntimeException("Fast creation")
// Avoid creating exceptions in hot paths when possible
fun validateInput(value: String): Boolean {
// Prefer returning boolean over throwing exception for validation
return value.isNotBlank()
}
// Use exceptions for exceptional cases
fun processInput(value: String) {
if (!validateInput(value)) {
throw IllegalArgumentException("Input cannot be blank")
}
// Process valid input
}// Exception handling has minimal overhead in WASI
try {
riskyOperation()
} catch (e: Exception) {
// Minimal catch overhead
handleError(e)
}
// However, avoid exceptions for control flow
// Bad:
fun findUser(id: String): User {
try {
return users[id] ?: throw NotFoundException()
} catch (e: NotFoundException) {
return createDefaultUser()
}
}
// Good:
fun findUser(id: String): User {
return users[id] ?: createDefaultUser()
}// DO: Create specific exception types for different error conditions
class UserNotFoundException(userId: String) : Exception("User not found: $userId")
class InvalidCredentialsException : Exception("Invalid username or password")
// DO: Include relevant context in exception messages
class ProcessingException(
val stage: String,
val itemId: String,
cause: Throwable
) : Exception("Processing failed at stage '$stage' for item '$itemId'", cause)
// DON'T: Use generic exceptions for specific problems
// throw Exception("Something went wrong") // Too generic// DO: Handle exceptions at appropriate levels
class UserService {
fun createUser(userData: UserData): User {
try {
validateUserData(userData)
return saveUser(userData)
} catch (e: ValidationException) {
// Handle validation errors specifically
throw UserCreationException("Invalid user data", e)
} catch (e: DatabaseException) {
// Handle database errors specifically
throw UserCreationException("Database error during user creation", e)
}
}
}
// DO: Use finally blocks for cleanup
fun processFile(filename: String) {
var resource: FileResource? = null
try {
resource = openFile(filename)
processResource(resource)
} catch (e: IOException) {
logger.error("File processing failed", e)
throw ProcessingException("Failed to process file: $filename", e)
} finally {
resource?.close()
}
}// Good exception logging practices
class ErrorHandler {
fun handleError(operation: String, error: Throwable) {
// Log with appropriate level
when (error) {
is ValidationException -> {
logger.warn("Validation failed in $operation: ${error.message}")
}
is SecurityException -> {
logger.error("Security violation in $operation", error)
}
else -> {
logger.error("Unexpected error in $operation", error)
}
}
// Include relevant context
logger.error("Error context - Operation: $operation, Time: ${Instant.now()}")
// Log exception chain
var cause = error.cause
while (cause != null) {
logger.error("Caused by: ${cause::class.simpleName}: ${cause.message}")
cause = cause.cause
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-stdlib-wasm-wasi