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: new runtime info API #437

Merged
merged 5 commits into from
Oct 5, 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
83 changes: 83 additions & 0 deletions packages/graalvm/api/graalvm.api
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ public final class elide/runtime/core/HostPlatformKt {
public static final fun isUnix (Lelide/runtime/core/HostPlatform$OperatingSystem;)Z
}

public abstract interface class elide/runtime/core/HostRuntime {
public abstract fun getName ()Ljava/lang/String;
public abstract fun getVariant ()Ljava/lang/String;
public abstract fun getVersion ()Lelide/runtime/core/Version;
}

public final class elide/runtime/core/HostRuntimeKt {
public static final fun on (Lelide/runtime/core/HostRuntime;Lelide/runtime/core/Version$Range;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
public static final fun on (Lelide/runtime/core/HostRuntime;Lelide/runtime/core/Version;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
}

public abstract interface class elide/runtime/core/PluginRegistry {
public abstract fun install (Lelide/runtime/core/EnginePlugin;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static synthetic fun install$default (Lelide/runtime/core/PluginRegistry;Lelide/runtime/core/EnginePlugin;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object;
Expand Down Expand Up @@ -149,6 +160,7 @@ public abstract class elide/runtime/core/PolyglotEngineConfiguration : elide/run
public final fun getEnvironment ()Ljava/util/Map;
public final fun getHostAccess ()Lelide/runtime/core/PolyglotEngineConfiguration$HostAccess;
public final fun getHostPlatform ()Lelide/runtime/core/HostPlatform;
public abstract fun getHostRuntime ()Lelide/runtime/core/HostRuntime;
public final fun setHostAccess (Lelide/runtime/core/PolyglotEngineConfiguration$HostAccess;)V
}

Expand All @@ -162,6 +174,55 @@ public final class elide/runtime/core/PolyglotEngineConfiguration$HostAccess : j
public static fun values ()[Lelide/runtime/core/PolyglotEngineConfiguration$HostAccess;
}

public final class elide/runtime/core/Version : java/lang/Comparable {
public static final field Companion Lelide/runtime/core/Version$Companion;
public fun <init> (III)V
public synthetic fun <init> (IIIILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun compareTo (Lelide/runtime/core/Version;)I
public synthetic fun compareTo (Ljava/lang/Object;)I
public final fun component1 ()I
public final fun component2 ()I
public final fun component3 ()I
public final fun copy (III)Lelide/runtime/core/Version;
public static synthetic fun copy$default (Lelide/runtime/core/Version;IIIILjava/lang/Object;)Lelide/runtime/core/Version;
public fun equals (Ljava/lang/Object;)Z
public final fun getMajor ()I
public final fun getMinor ()I
public final fun getPatch ()I
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class elide/runtime/core/Version$Companion {
public final fun getZero ()Lelide/runtime/core/Version;
public final fun parse (Ljava/lang/String;)Lelide/runtime/core/Version;
}

public final class elide/runtime/core/Version$Range {
public fun <init> ()V
public fun <init> (Lelide/runtime/core/Version;Lelide/runtime/core/Version;ZZ)V
public synthetic fun <init> (Lelide/runtime/core/Version;Lelide/runtime/core/Version;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lelide/runtime/core/Version;
public final fun component2 ()Lelide/runtime/core/Version;
public final fun component3 ()Z
public final fun component4 ()Z
public final fun contains (Lelide/runtime/core/Version;)Z
public final fun copy (Lelide/runtime/core/Version;Lelide/runtime/core/Version;ZZ)Lelide/runtime/core/Version$Range;
public static synthetic fun copy$default (Lelide/runtime/core/Version$Range;Lelide/runtime/core/Version;Lelide/runtime/core/Version;ZZILjava/lang/Object;)Lelide/runtime/core/Version$Range;
public fun equals (Ljava/lang/Object;)Z
public final fun getIncludeMax ()Z
public final fun getIncludeMin ()Z
public final fun getMax ()Lelide/runtime/core/Version;
public final fun getMin ()Lelide/runtime/core/Version;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class elide/runtime/core/VersionKt {
public static final fun andHigher (Lelide/runtime/core/Version;)Lelide/runtime/core/Version$Range;
public static final fun andLower (Lelide/runtime/core/Version;)Lelide/runtime/core/Version$Range;
}

public final class elide/runtime/core/extensions/ContextBuilderKt {
public static final fun disableOption (Lorg/graalvm/polyglot/Context$Builder;Ljava/lang/String;)V
public static final fun disableOptions (Lorg/graalvm/polyglot/Context$Builder;[Ljava/lang/String;)V
Expand All @@ -186,6 +247,28 @@ public final class elide/runtime/core/extensions/ExecutionListenerKt {
public static final fun attach (Lorg/graalvm/polyglot/management/ExecutionListener$Builder;Lelide/runtime/core/PolyglotEngine;)Lorg/graalvm/polyglot/management/ExecutionListener;
}

public final class elide/runtime/core/internals/graalvm/GraalVMRuntime : elide/runtime/core/HostRuntime {
public static final field Companion Lelide/runtime/core/internals/graalvm/GraalVMRuntime$Companion;
public static final field VARIANT_JVM Ljava/lang/String;
public static final field VARIANT_NATIVE Ljava/lang/String;
public fun <init> ()V
public fun <init> (Lelide/runtime/core/Version;Ljava/lang/String;)V
public synthetic fun <init> (Lelide/runtime/core/Version;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getName ()Ljava/lang/String;
public final fun getNative ()Z
public fun getVariant ()Ljava/lang/String;
public fun getVersion ()Lelide/runtime/core/Version;
}

public final class elide/runtime/core/internals/graalvm/GraalVMRuntime$Companion {
public final fun getGVM_23 ()Lelide/runtime/core/Version;
public final fun getGVM_23_1 ()Lelide/runtime/core/Version;
}

public final class elide/runtime/core/internals/graalvm/GraalVMRuntimeKt {
public static final fun isNativeImage (Lelide/runtime/core/HostRuntime;)Z
}

public abstract interface class elide/runtime/feature/FrameworkFeature : org/graalvm/nativeimage/hosted/Feature {
public fun findClassesInJar (Ljava/net/JarURLConnection;Ljava/lang/String;)Ljava/util/List;
public abstract fun getDescription ()Ljava/lang/String;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2023 Elide Ventures, LLC.
*
* 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.core

/** Provides information about the runtime hosting the application. */
@DelicateElideApi public interface HostRuntime {
/** The name of this runtime, which is used in string representations and for debugging purposes. */
public val name: String

/** The version spec of this runtime, wrapped to allow version comparisons using regular operators. */
public val version: Version?

/** An optional variant name for this runtime. Some engines like GraalVM differentiate between runtime variants. */
public val variant: String?
}

/**
* Execute a [block] and return its result if this runtime's is running the provided [version], otherwise return
* `null`. If this runtime's version can't be detected, `null` will be returned.
*/
@DelicateElideApi public inline fun <R> HostRuntime.on(version: Version, block: () -> R): R? {
return if (this.version == version) block() else null
}

/**
* Execute a [block] and return its result if this runtime's version is in the provided [range], otherwise return
* `null`. If this runtime's version can't be detected, `null` will be returned.
*/
@DelicateElideApi public inline fun <R> HostRuntime.on(range: Version.Range, block: () -> R): R? {
return version?.takeIf { it in range }?.let { block() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ import elide.runtime.core.PolyglotEngineConfiguration.HostAccess.ALLOW_NONE
/** Restrict all access to the host environment. */
ALLOW_NONE,
}

/** The access granted to guest code over host resources, such as environment variables and the file system. */
public var hostAccess: HostAccess = ALLOW_NONE

/** Information about the platform hosting the runtime. */
public val hostPlatform: HostPlatform = HostPlatform.resolve()

/** The access granted to guest code over host resources, such as environment variables and the file system. */
public var hostAccess: HostAccess = ALLOW_NONE
/** Information about the runtime engine. */
public abstract val hostRuntime: HostRuntime

/** Environment to apply to the context. */
public val environment: MutableMap<String, String> = ConcurrentSkipListMap()
Expand Down
119 changes: 119 additions & 0 deletions packages/graalvm/src/main/kotlin/elide/runtime/core/Version.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2023 Elide Ventures, LLC.
*
* 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.core

import elide.runtime.core.Version.Companion.parse
import elide.runtime.core.Version.Range

/**
* Represents a semantic version that can be compared with others or used to form a version range. Version objects are
* used to enable version-aware behavior in engine plugins.
*
* Use [toString] if a full version string is required instead of separate components.
*
* @see parse
*/
@DelicateElideApi public data class Version(
/** Major version component. */
public val major: Int,
/** Minor version component. */
public val minor: Int = 0,
/** Patch version component. */
public val patch: Int = 0,
) : Comparable<Version> {

/**
* A range of [versions][Version], which can be [queried][contains] to see if a [Version] falls in a specific
* range. Use the normal range operators or extensions to create version ranges.
*
* By default, a range includes every valid version (from [Zero] onwards).
*/
@DelicateElideApi public data class Range(
public val min: Version = Zero,
public val max: Version? = null,
public val includeMin: Boolean = true,
public val includeMax: Boolean = true,
) {
public operator fun contains(version: Version): Boolean {
val minResult = version.compareTo(min)
val maxResult = max?.let { version.compareTo(it) }

return when (includeMin) {
true -> when (includeMax) {
true -> minResult >= 0 && (maxResult == null || maxResult <= 0)
false -> minResult >= 0 && (maxResult == null || maxResult < 0)
}

false -> when (includeMax) {
true -> minResult > 0 && (maxResult == null || maxResult <= 0)
false -> minResult > 0 && (maxResult == null || maxResult < 0)
}
}
}
}

public override operator fun compareTo(other: Version): Int {
major.compareTo(other.major).takeIf { it != 0 }?.let { return it }
minor.compareTo(other.minor).takeIf { it != 0 }?.let { return it }
patch.compareTo(other.patch).takeIf { it != 0 }?.let { return it }
return 0
}

override fun toString(): String {
return "$major.$minor.$patch"
}

public companion object {
/** Number of segments required in a semantic version string (major and minor, patch defaults to 0). */
private const val MIN_SEGMENTS = 2

/** Regular expression used to replace characters with version separators. */
private val SpecRegex = Regex("[-_]")

/** A placeholder "zero" version provided for convenience. */
public val Zero: Version by lazy { Version(0, 0, 0) }


/**
* Parse a string [spec] and return a rich [Version] object.
*
* @return A parsed [Version] containing the [major], [minor], and [patch] fields.
* @throws IllegalArgumentException if the provided [spec] is not a semantic version.
*/
public fun parse(spec: String): Version = runCatching {
// replace '-' and '_' with '.', then split and check for basic contraints
val parts = spec.replace(SpecRegex, ".").split('.')
check(parts.size >= MIN_SEGMENTS) { "Semantic version should have at least 'major' and 'minor' components" }

// parse the digits, only major and minor components are required
Version(
major = parts[0].toInt(),
minor = parts[1].toInt(),
patch = parts.getOrNull(2)?.toInt() ?: 0,
)
}.getOrElse { cause ->
throw IllegalArgumentException("Invalid semantic version '$spec'", cause)
}
}
}

/** Returns a [version range][Range] including this and every higher version. */
@DelicateElideApi public fun Version.andHigher(): Range {
return Range(this)
}

/** Returns a [version range][Range] including this and every lower version. */
@DelicateElideApi public fun Version.andLower(): Range {
return Range(max = this)
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ import elide.runtime.core.internals.MutableEngineLifecycle
/** A set of languages enabled for use in the engine. */
internal val languages: Set<GuestLanguage> get() = langs

/** Runtime info, resolved from GraalVM static properties. */
override val hostRuntime: HostRuntime = GraalVMRuntime()

override fun <C : Any, I : Any> install(plugin: EnginePlugin<C, I>, configure: C.() -> Unit): I {
require(plugin.key.id !in plugins) { "A plugin with the provided key is already registered" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,18 @@ import java.util.logging.LogRecord
import kotlin.io.path.Path
import elide.runtime.Logger
import elide.runtime.Logging
import elide.runtime.core.DelicateElideApi
import elide.runtime.core.EngineLifecycleEvent
import elide.runtime.core.*
import elide.runtime.core.EngineLifecycleEvent.EngineCreated
import elide.runtime.core.EngineLifecycleEvent.EngineInitialized
import elide.runtime.core.PolyglotContext
import elide.runtime.core.PolyglotEngine
import elide.runtime.core.PolyglotEngineConfiguration.HostAccess
import elide.runtime.core.PolyglotEngineConfiguration.HostAccess.*
import elide.runtime.core.extensions.disableOption
import elide.runtime.core.extensions.enableOption
import elide.runtime.core.extensions.enableOptions
import elide.runtime.core.internals.MutableEngineLifecycle
import elide.runtime.core.internals.graalvm.GraalVMEngine.Companion.create
import elide.runtime.gvm.internals.VMStaticProperty
import elide.runtime.core.internals.graalvm.GraalVMRuntime.Companion.GVM_23
import elide.runtime.core.internals.graalvm.GraalVMRuntime.Companion.GVM_23_1
import org.graalvm.polyglot.HostAccess as PolyglotHostAccess

/**
Expand Down Expand Up @@ -215,16 +213,23 @@ import org.graalvm.polyglot.HostAccess as PolyglotHostAccess
// TODO(@darvld): swap for throughput in server mode
option("engine.Mode", "latency")

// TODO(@darvld): translate this to an API in the core package
listOfNotNull(
VMStaticProperty.activeWhenAtMost("23.0", "engine.Inlining"),
VMStaticProperty.activeWhenAtMost("23.0", "engine.InlineAcrossTruffleBoundary"),
VMStaticProperty.activeWhenAtLeast("23.1", "compiler.Inlining"),
VMStaticProperty.activeWhenAtLeast("23.1", "compiler.EncodedGraphCache"),
VMStaticProperty.activeWhenAtLeast("23.1", "compiler.InlineAcrossTruffleBoundary"),
VMStaticProperty.inactiveWhenAtLeast("23.1", "engine.WarnOptionDeprecation"),
).forEach {
option(it.symbol, it.value())
configuration.hostRuntime.on(GVM_23.andLower()) {
enableOptions(
"engine.Inlining",
"engine.InlineAcrossTruffleBoundary",
)
}

configuration.hostRuntime.on(GVM_23_1.andHigher()) {
enableOptions(
"engine.Inlining",
"engine.InlineAcrossTruffleBoundary",
"compiler.Inlining",
"compiler.EncodedGraphCache",
"compiler.InlineAcrossTruffleBoundary",
)

disableOption("engine.WarnOptionDeprecation")
}

// isolate options
Expand Down
Loading