Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public interface PolyglotContext {
*
* @param source The guest code to be executed.
* @param internals Whether to allow access to internal runtime features; the provided [source] must be marked as
* internal to enable this acesss.
* internal to enable this access.
* @return The result of evaluating the [source].
*/
public fun evaluate(source: Source, internals: Boolean): PolyglotValue =
Expand Down
31 changes: 31 additions & 0 deletions packages/graalvm/api/graalvm.api
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,14 @@ public final class elide/runtime/gvm/internals/intrinsics/ElideIntrinsicKt {
public static final fun installElideBuiltin (Ljava/lang/String;Ljava/lang/Object;)V
}

public abstract class elide/runtime/gvm/internals/intrinsics/js/AbstractJsIntrinsic : elide/runtime/intrinsics/GuestIntrinsic {
public fun <init> ()V
public fun displayName ()Ljava/lang/String;
public fun language ()Lelide/runtime/gvm/GuestLanguage;
public fun symbolicName ()Ljava/lang/String;
public fun toString ()Ljava/lang/String;
}

public synthetic class elide/runtime/gvm/internals/intrinsics/js/abort/$AbortControllerIntrinsic$Definition : io/micronaut/context/AbstractInitializableBeanDefinitionAndReference {
public static final field $ANNOTATION_METADATA Lio/micronaut/core/annotation/AnnotationMetadata;
public fun <init> ()V
Expand Down Expand Up @@ -5771,6 +5779,29 @@ public abstract interface class elide/runtime/intrinsics/testing/TestingAPI$Test
public abstract interface class elide/runtime/intrinsics/testing/TestingAPI$TestGraphNode$Test : elide/runtime/intrinsics/testing/TestingAPI$TestGraphNode {
}

public synthetic class elide/runtime/javascript/$QueueMicrotaskCallable$Definition : io/micronaut/context/AbstractInitializableBeanDefinitionAndReference {
public static final field $ANNOTATION_METADATA Lio/micronaut/core/annotation/AnnotationMetadata;
public fun <init> ()V
protected fun <init> (Ljava/lang/Class;Lio/micronaut/context/AbstractInitializableBeanDefinition$MethodOrFieldReference;)V
public fun instantiate (Lio/micronaut/context/BeanResolutionContext;Lio/micronaut/context/BeanContext;)Ljava/lang/Object;
public fun isEnabled (Lio/micronaut/context/BeanContext;)Z
public fun isEnabled (Lio/micronaut/context/BeanContext;Lio/micronaut/context/BeanResolutionContext;)Z
public fun load ()Lio/micronaut/inject/BeanDefinition;
}

public final synthetic class elide/runtime/javascript/$QueueMicrotaskCallable$Introspection : io/micronaut/inject/beans/AbstractInitializableBeanIntrospectionAndReference {
public static final field $ANNOTATION_METADATA Lio/micronaut/core/annotation/AnnotationMetadata;
public fun <init> ()V
public fun hasBuilder ()Z
public fun isBuildable ()Z
}

public final class elide/runtime/javascript/QueueMicrotaskCallable : elide/runtime/gvm/internals/intrinsics/js/AbstractJsIntrinsic, org/graalvm/polyglot/proxy/ProxyExecutable {
public fun <init> (Lelide/runtime/exec/GuestExecutorProvider;)V
public fun execute ([Lorg/graalvm/polyglot/Value;)Ljava/lang/Object;
public fun install (Lelide/runtime/intrinsics/GuestIntrinsic$MutableIntrinsicBindings;)V
}

public synthetic class elide/runtime/node/asserts/$NodeAssertModule$Definition : io/micronaut/context/AbstractInitializableBeanDefinitionAndReference {
public static final field $ANNOTATION_METADATA Lio/micronaut/core/annotation/AnnotationMetadata;
public fun <init> ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import elide.runtime.gvm.GraalVMGuest
import elide.runtime.intrinsics.GuestIntrinsic

/** Abstract base class for all intrinsic implementations. */
internal abstract class AbstractJsIntrinsic : GuestIntrinsic {
public abstract class AbstractJsIntrinsic : GuestIntrinsic {
override fun language(): GuestLanguage = GraalVMGuest.JAVASCRIPT
override fun symbolicName(): String = "native code"
@Deprecated("Use symbolicName instead", ReplaceWith("symbolicName"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2024-2025 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.
*/
@file:OptIn(DelicateElideApi::class)

package elide.runtime.javascript

import elide.runtime.exec.GuestExecutorProvider
import elide.runtime.gvm.api.Intrinsic
import elide.runtime.gvm.internals.intrinsics.js.AbstractJsIntrinsic
import elide.runtime.gvm.js.JsError
import elide.runtime.intrinsics.GuestIntrinsic
import jakarta.inject.Inject
import jakarta.inject.Singleton
import org.graalvm.polyglot.Value
import org.graalvm.polyglot.proxy.ProxyExecutable
import elide.runtime.core.DelicateElideApi
import elide.runtime.gvm.js.JsSymbol.JsSymbols.asPublicJsSymbol
import elide.runtime.gvm.js.undefined

// Name of the `queueMicrotask` function in the global scope.
private const val QUEUE_MICROTASK_NAME = "queueMicrotask"

// Public JavaScript symbol for the `queueMicrotask` function.
private val QUEUE_MICROTASK_SYMBOL = QUEUE_MICROTASK_NAME.asPublicJsSymbol()

/**
* ## Queue Microtask Callable
*
* Mounts a callable intrinsic function at the name `queueMicrotask`, in compliance with Web JavaScript standards which
* expect this function to be available in the global scope. The `queueMicrotask` function is used to queue a chunk of
* code to execute safely on the JavaScript event loop.
*
* [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/queueMicrotask)
*/
@Singleton
@Intrinsic(QUEUE_MICROTASK_NAME) public class QueueMicrotaskCallable @Inject constructor (
private val executorProvider: GuestExecutorProvider,
) : ProxyExecutable, AbstractJsIntrinsic() {
override fun install(bindings: GuestIntrinsic.MutableIntrinsicBindings) {
bindings[QUEUE_MICROTASK_SYMBOL] = this
}

internal operator fun invoke(callable: () -> Unit) {
executorProvider.executor().execute {
callable.invoke()
}
}

override fun execute(vararg arguments: Value?): Any? {
val first = arguments.firstOrNull() ?: throw JsError.typeError("First argument to `queueMicrotask` is required")
if (!first.canExecute()) throw JsError.typeError("First argument to `queueMicrotask` must be a function")
invoke(first::executeVoid)
return undefined()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 Elide Technologies, Inc.
* Copyright (c) 2024-2025 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
Expand Down Expand Up @@ -61,7 +61,7 @@ import elide.runtime.core.evaluate
* [PolyglotContext.evaluate] and selecting [JavaScript] as source language.
*
* @param source The interpreted JavaScript source code to be executed.
* @return The result of the invocation. If [esm] is `true`, an object is returned, with exported values as members.
* @return The result of the invocation; an object is returned, with exported values as members.
*/
@DelicateElideApi public fun PolyglotContext.javascript(source: Source): PolyglotValue =
evaluate(source)
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private const val ENABLE_SUPPRESSIONS = true
"isNaN",
"parseFloat",
"parseInt",
"queueMicrotask",
"decodeURI",
"decodeURIComponent",
"encodeURI",
Expand Down Expand Up @@ -158,7 +159,6 @@ private const val ENABLE_SUPPRESSIONS = true
"PerformanceObserverEntryList",
"performance",
"process",
"queueMicrotask",
"ReadableByteStreamController",
"ReadableStream",
"ReadableStreamBYOBReader",
Expand Down Expand Up @@ -214,7 +214,6 @@ private const val ENABLE_SUPPRESSIONS = true
"navigator", // not yet implemented
"setImmediate", // not yet implemented
"clearImmediate", // not yet implemented
"queueMicrotask", // not yet implemented
"structuredClone", // not yet implemented
"InternalError", // web-standard only, not present in non-browser runtimes
"BroadcastChannel", // not yet implemented
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2024-2025 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.javascript

import org.graalvm.polyglot.Value
import org.graalvm.polyglot.proxy.ProxyExecutable
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertNotNull
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertTrue
import elide.annotations.Inject
import elide.runtime.core.DelicateElideApi
import elide.runtime.exec.GuestExecution
import elide.runtime.exec.GuestExecutorProvider
import elide.runtime.gvm.internals.js.AbstractJsIntrinsicTest
import elide.runtime.gvm.js.undefined
import elide.runtime.intrinsics.js.err.TypeError
import elide.runtime.plugins.js.javascript
import elide.testing.annotations.Test
import elide.testing.annotations.TestCase

@TestCase internal class QueueMicrotaskTest : AbstractJsIntrinsicTest<QueueMicrotaskCallable>() {
@Inject lateinit var queueMicrotask: QueueMicrotaskCallable
override fun provide(): QueueMicrotaskCallable = queueMicrotask

@Test override fun testInjectable() {
assertNotNull(queueMicrotask)
}

@Test fun testExecMicrotask() {
val exec = GuestExecution.direct()
val prov = GuestExecutorProvider { exec }
val fresh = QueueMicrotaskCallable(prov)
var didExec = false
val invocable = { didExec = true }
assertDoesNotThrow { fresh.invoke(invocable) }
assertTrue(didExec)
}

@Test fun testExecMicrotaskGuest() = dual {
val exec = GuestExecution.direct()
val prov = GuestExecutorProvider { exec }
val fresh = QueueMicrotaskCallable(prov)
var didExec = false
val invocable = { didExec = true }
assertDoesNotThrow { fresh.invoke(invocable) }
assertTrue(didExec)
}.guest {
// language=JavaScript
"""
let didExec = false;
queueMicrotask(() => didExec = true);
test(didExec).isEqualTo(true);
"""
}

@OptIn(DelicateElideApi::class)
@Test fun testExecMicrotaskGuestDirect() {
val exec = GuestExecution.direct()
val prov = GuestExecutorProvider { exec }
val fresh = QueueMicrotaskCallable(prov)
val guestFn = withContext {
javascript(
// language=JavaScript
"""
const fn = (() => {
// hello
});
fn;
"""
)
}

assertNotNull(guestFn)
assertDoesNotThrow { fresh.execute(guestFn) }
}

@Test fun testExecMicrotaskRejectsNulls() {
assertThrows<TypeError> { queueMicrotask.execute(Value.asValue(null)) }
}

@Test fun testExecMicrotaskRejectsNonExecutable() {
assertThrows<TypeError> { queueMicrotask.execute(Value.asValue(5)) }
}

@Test fun testExecMicrotaskRejectsNoArgs() {
assertThrows<TypeError> { queueMicrotask.execute() }
}
}
Loading