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

feat(node): custom Buffer implementation #1072

Merged
merged 11 commits into from
Jul 21, 2024
132 changes: 87 additions & 45 deletions packages/graalvm/api/graalvm.api
Original file line number Diff line number Diff line change
Expand Up @@ -3467,53 +3467,95 @@ public abstract interface class elide/runtime/intrinsics/js/node/asserts/Asserti
public abstract fun getOperator ()Ljava/lang/String;
}

public abstract interface class elide/runtime/intrinsics/js/node/buffer/Blob {
}

public abstract interface class elide/runtime/intrinsics/js/node/buffer/Buffer : java/io/Serializable, org/graalvm/polyglot/proxy/ProxyArray, org/graalvm/polyglot/proxy/ProxyInstantiable, org/graalvm/polyglot/proxy/ProxyIterable, org/graalvm/polyglot/proxy/ProxyObject {
public abstract fun getLength ()I
public synthetic fun getMemberKeys ()Ljava/lang/Object;
public fun getMemberKeys ()[Ljava/lang/String;
public fun hasMember (Ljava/lang/String;)Z
public fun putMember (Ljava/lang/String;Lorg/graalvm/polyglot/Value;)V
}

public abstract interface class elide/runtime/intrinsics/js/node/buffer/Buffer$BufferConstructors {
public abstract fun create (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun create (Lorg/graalvm/polyglot/Value;Ljava/lang/String;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public static synthetic fun create$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferConstructors;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public static synthetic fun create$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferConstructors;Lorg/graalvm/polyglot/Value;Ljava/lang/String;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
}

public abstract interface class elide/runtime/intrinsics/js/node/buffer/Buffer$BufferFactories : elide/runtime/intrinsics/js/node/buffer/Buffer$BufferUtilities {
public abstract fun alloc (ILorg/graalvm/polyglot/Value;Ljava/lang/String;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public static synthetic fun alloc$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferFactories;ILorg/graalvm/polyglot/Value;Ljava/lang/String;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun allocUnsafe (I)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun allocUnsafeSlow (I)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun from (Lelide/runtime/intrinsics/js/node/buffer/Buffer;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun from (Ljava/lang/String;Ljava/lang/String;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun from (Lorg/graalvm/polyglot/Value;ILjava/lang/Integer;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun from (Lorg/graalvm/polyglot/Value;Lorg/graalvm/polyglot/Value;Lorg/graalvm/polyglot/Value;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun from ([Ljava/lang/Byte;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public static synthetic fun from$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferFactories;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public static synthetic fun from$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferFactories;Lorg/graalvm/polyglot/Value;ILjava/lang/Integer;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public static synthetic fun from$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferFactories;Lorg/graalvm/polyglot/Value;Lorg/graalvm/polyglot/Value;Lorg/graalvm/polyglot/Value;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
}

public abstract interface class elide/runtime/intrinsics/js/node/buffer/Buffer$BufferStatics : elide/runtime/intrinsics/js/node/buffer/Buffer$BufferConstructors, elide/runtime/intrinsics/js/node/buffer/Buffer$BufferFactories {
}

public abstract interface class elide/runtime/intrinsics/js/node/buffer/Buffer$BufferUtilities {
public abstract fun byteLength (Ljava/lang/String;Ljava/lang/String;)I
public static synthetic fun byteLength$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferUtilities;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)I
public abstract fun compare (Lelide/runtime/intrinsics/js/node/buffer/Buffer;Lelide/runtime/intrinsics/js/node/buffer/Buffer;)I
public abstract fun concat ([Lelide/runtime/intrinsics/js/node/buffer/Buffer;Ljava/lang/Integer;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public static synthetic fun concat$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferUtilities;[Lelide/runtime/intrinsics/js/node/buffer/Buffer;Ljava/lang/Integer;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract fun copyBytesFrom (Lelide/runtime/intrinsics/js/node/buffer/Buffer;II)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public static synthetic fun copyBytesFrom$default (Lelide/runtime/intrinsics/js/node/buffer/Buffer$BufferUtilities;Lelide/runtime/intrinsics/js/node/buffer/Buffer;IIILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/Buffer;
public abstract interface class elide/runtime/intrinsics/js/node/buffer/BufferClass : org/graalvm/polyglot/proxy/ProxyInstantiable, org/graalvm/polyglot/proxy/ProxyObject {
public abstract fun alloc (ILorg/graalvm/polyglot/Value;Ljava/lang/String;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public static synthetic fun alloc$default (Lelide/runtime/intrinsics/js/node/buffer/BufferClass;ILorg/graalvm/polyglot/Value;Ljava/lang/String;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun allocUnsafe (I)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun allocUnsafeSlow (I)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun byteLength (Lorg/graalvm/polyglot/Value;Ljava/lang/String;)I
public abstract fun compare (Lorg/graalvm/polyglot/Value;Lorg/graalvm/polyglot/Value;)I
public abstract fun concat (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public static synthetic fun concat$default (Lelide/runtime/intrinsics/js/node/buffer/BufferClass;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun copyBytesFrom (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public static synthetic fun copyBytesFrom$default (Lelide/runtime/intrinsics/js/node/buffer/BufferClass;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun from (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public static synthetic fun from$default (Lelide/runtime/intrinsics/js/node/buffer/BufferClass;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun getPoolSize ()I
public abstract fun isBuffer (Ljava/lang/Object;)Z
public abstract fun isBuffer (Lorg/graalvm/polyglot/Value;)Z
public abstract fun isEncoding (Ljava/lang/String;)Z
public abstract fun setPoolSize (I)V
}

public abstract interface class elide/runtime/intrinsics/js/node/buffer/BufferInstance : org/graalvm/polyglot/proxy/ProxyArray, org/graalvm/polyglot/proxy/ProxyObject {
public abstract fun compare (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;)I
public static synthetic fun compare$default (Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)I
public abstract fun copy (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;)I
public static synthetic fun copy$default (Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)I
public abstract fun entries ()Lorg/graalvm/polyglot/Value;
public abstract fun fill (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public static synthetic fun fill$default (Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun getBuffer ()Lorg/graalvm/polyglot/Value;
public abstract fun getByteOffset ()I
public abstract fun getLength ()I
public abstract fun indexOf (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/String;)I
public static synthetic fun indexOf$default (Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/String;ILjava/lang/Object;)I
public abstract fun keys ()Lorg/graalvm/polyglot/Value;
public abstract fun lastIndexOf (Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/String;)I
public static synthetic fun lastIndexOf$default (Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;Lorg/graalvm/polyglot/Value;Ljava/lang/Integer;Ljava/lang/String;ILjava/lang/Object;)I
public abstract fun readBigInt64BE (Ljava/lang/Integer;)J
public abstract fun readBigInt64LE (Ljava/lang/Integer;)J
public fun readBigUInt64BE (Ljava/lang/Integer;)Lcom/oracle/truffle/js/runtime/BigInt;
public fun readBigUInt64LE (Ljava/lang/Integer;)Lcom/oracle/truffle/js/runtime/BigInt;
public abstract fun readDoubleBE (Ljava/lang/Integer;)D
public abstract fun readDoubleLE (Ljava/lang/Integer;)D
public abstract fun readFloatBE (Ljava/lang/Integer;)F
public abstract fun readFloatLE (Ljava/lang/Integer;)F
public abstract fun readInt16BE (Ljava/lang/Integer;)S
public abstract fun readInt16LE (Ljava/lang/Integer;)S
public abstract fun readInt32BE (Ljava/lang/Integer;)I
public abstract fun readInt32LE (Ljava/lang/Integer;)I
public abstract fun readInt8 (Ljava/lang/Integer;)B
public abstract fun readIntBE (II)J
public abstract fun readIntLE (II)J
public fun readUInt16BE (Ljava/lang/Integer;)I
public fun readUInt16LE (Ljava/lang/Integer;)I
public fun readUInt32BE (Ljava/lang/Integer;)J
public fun readUInt32LE (Ljava/lang/Integer;)J
public fun readUInt8 (Ljava/lang/Integer;)I
public abstract fun readUIntBE (II)J
public abstract fun readUIntLE (II)J
public abstract fun subarray (Ljava/lang/Integer;Ljava/lang/Integer;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public static synthetic fun subarray$default (Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;Ljava/lang/Integer;Ljava/lang/Integer;ILjava/lang/Object;)Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun swap16 ()Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun swap32 ()Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun swap64 ()Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;
public abstract fun toJSON ()Lorg/graalvm/polyglot/Value;
public abstract fun toString (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/String;
public abstract fun values ()Lorg/graalvm/polyglot/Value;
public abstract fun write (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;)I
public static synthetic fun write$default (Lelide/runtime/intrinsics/js/node/buffer/BufferInstance;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;ILjava/lang/Object;)I
public abstract fun writeBigInt64BE (JLjava/lang/Integer;)I
public abstract fun writeBigInt64LE (JLjava/lang/Integer;)I
public fun writeBigUInt64BE (JLjava/lang/Integer;)I
public fun writeBigUInt64LE (JLjava/lang/Integer;)I
public abstract fun writeDoubleBE (DLjava/lang/Integer;)I
public abstract fun writeDoubleLE (DLjava/lang/Integer;)I
public abstract fun writeFloatBE (FLjava/lang/Integer;)I
public abstract fun writeFloatLE (FLjava/lang/Integer;)I
public abstract fun writeInt16BE (SLjava/lang/Integer;)I
public abstract fun writeInt16LE (SLjava/lang/Integer;)I
public abstract fun writeInt32BE (ILjava/lang/Integer;)I
public abstract fun writeInt32LE (ILjava/lang/Integer;)I
public abstract fun writeInt8 (BLjava/lang/Integer;)I
public abstract fun writeIntBE (JII)I
public abstract fun writeIntLE (JII)I
public fun writeUInt16BE (ILjava/lang/Integer;)I
public fun writeUInt16LE (ILjava/lang/Integer;)I
public fun writeUInt32BE (JLjava/lang/Integer;)I
public fun writeUInt32LE (JLjava/lang/Integer;)I
public fun writeUInt8 (ILjava/lang/Integer;)I
public fun writeUIntBE (JII)I
public fun writeUIntLE (JII)I
}

public final class elide/runtime/intrinsics/js/node/events/AddEventListenerOptions {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2024 Elide Technologies, Inc.
*
* Licensed under the MIT license (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://opensource.org/license/mit/
*
* 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 elide.runtime.gvm.internals.node.buffer

import elide.runtime.core.DelicateElideApi
import elide.runtime.core.PolyglotValue

/**
* A wrapper around a `TypedArray` (e.g. `Uint8Array`), `Buffer`, or any view-like value which exposes an inner
* `ArrayBuffer`, offset, and length.
*
* This wrapper can be used to easily interact with typed arrays and their inner array buffers. Use [tryFrom] to
* attempt wrapping a value without throwing an exception for invalid cases.
*/
@DelicateElideApi @JvmInline internal value class GuestBufferView private constructor(val value: PolyglotValue) {
/**
* Returns the array buffer backing this view, as a [GuestBytes] wrapper. The returned value is validated and is
* guaranteed to contain buffer elements.
*/
fun bytes(): GuestBytes {
val bytes = value.getMember(MEMBER_BUFFER)
check(bytes.hasBufferElements()) { "Expected the view's backing buffer to have buffer elements" }
return GuestBytes(bytes)
}

/** Returns the offset in the backing array buffer at which this view starts. */
fun byteOffset(): Int {
return value.getMember(MEMBER_OFFSET).asInt()
}

/** Returns the size in bytes of this view. */
fun byteSize(): Int {
return value.getMember(MEMBER_LENGTH).asInt()
}

operator fun component1(): GuestBytes = bytes()

operator fun component2(): Int = byteOffset()

operator fun component3(): Int = byteSize()

companion object {
/** Member name for the backing buffer value. */
private const val MEMBER_BUFFER = "buffer"

/** Member name for the view's byte offset. */
private const val MEMBER_OFFSET = "byteOffset"

/** Member name for the view's byte length. */
private const val MEMBER_LENGTH = "byteLength"

/**
* If the given [value] is a buffer view (e.g. `TypedArray`, `Buffer`), returns a [GuestBufferView] wrapping it,
* otherwise returns `null`.
*/
fun tryFrom(value: PolyglotValue): GuestBufferView? {
return if (!value.hasMembers() || !value.hasMember(MEMBER_BUFFER) || !value.hasMember(MEMBER_OFFSET)) null
else GuestBufferView(value)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2024 Elide Technologies, Inc.
*
* Licensed under the MIT license (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://opensource.org/license/mit/
*
* 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 elide.runtime.gvm.internals.node.buffer

import elide.runtime.core.DelicateElideApi
import elide.runtime.core.PolyglotValue

/**
* A light wrapper around a guest buffer-like value, providing indexed access. This helper can be used to wrap the
* `ArrayBuffer` instances backing a `Uint8Array` or similar, for convenience when manipulating them.
*
* Note that no validation of any kind is performed during construction or by any of the operators.
*/
@DelicateElideApi @JvmInline internal value class GuestBytes(val value: PolyglotValue) {
/** Returns the length in bytes of the wrapped value. */
val size: Int get() = value.bufferSize.toInt()

/** Returns the length in bytes of the wrapped value as a [Long], useful for certain operations. */
val longSize: Long get() = value.bufferSize

/** Returns the value of the wrapped buffer at the given [index]. */
operator fun get(index: Long): Byte {
return value.readBufferByte(index)
}

/** Returns the value of the wrapped buffer at the given [index]. */
operator fun get(index: Int): Byte {
return value.readBufferByte(index.toLong())
}

/** Sets the [value] of the wrapped buffer at the given [index]. */
operator fun set(index: Long, value: Byte) {
this.value.writeBufferByte(index, value)
}

/** Sets the [value] of the wrapped buffer at the given [index]. */
operator fun set(index: Int, value: Byte) {
this.value.writeBufferByte(index.toLong(), value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import elide.runtime.gvm.internals.intrinsics.js.JsSymbol.JsSymbols.asJsSymbol
import elide.runtime.gvm.internals.intrinsics.js.JsSymbol.JsSymbols.asPublicJsSymbol
import elide.runtime.intrinsics.GuestIntrinsic.MutableIntrinsicBindings
import elide.runtime.intrinsics.js.node.BufferAPI
import elide.runtime.intrinsics.js.node.buffer.BufferClass

/** Internal symbol where the bindings for the 'buffer' module are installed. */
private const val BUFFER_MODULE_SYMBOL_ROOT = "node_buffer"
Expand All @@ -38,6 +39,9 @@ private const val BLOB_SYMBOL = "Blob"
/** Symbol at which the [NodeBlob] class is installed. */
private const val FILE_SYMBOL = "File"

/** Symbol at which the [NodeBlob] class is installed. */
private const val BUFFER_TYPE_SYMBOL = "Buffer"

// Installs the Node `buffer` built-in module.
@Intrinsic internal class NodeBufferModule : AbstractNodeBuiltinModule() {
@Inject lateinit var facade: NodeBufferModuleFacade
Expand All @@ -47,6 +51,10 @@ private const val FILE_SYMBOL = "File"
bindings[BUFFER_MODULE_SYMBOL.asJsSymbol()] = facade
bindings[BLOB_SYMBOL.asPublicJsSymbol()] = NodeBlob::class.java
bindings[FILE_SYMBOL.asPublicJsSymbol()] = NodeFile::class.java

// A single NodeBufferClass instance acts as meta-object for the `Buffer` type;
// it will also be exposed as part of the `node:buffer` module by guest init code
bindings[BUFFER_TYPE_SYMBOL.asPublicJsSymbol()] = NodeBufferClass()
}
}

Expand Down
Loading
Loading