15
15
16
16
package elide.runtime.gvm.internals.node.os
17
17
18
+ import io.micronaut.core.annotation.Introspected
19
+ import io.micronaut.core.annotation.ReflectiveAccess
20
+ import org.graalvm.polyglot.Value
21
+ import org.graalvm.polyglot.proxy.ProxyExecutable
22
+ import org.graalvm.polyglot.proxy.ProxyObject
23
+ import org.jetbrains.annotations.VisibleForTesting
18
24
import oshi.SystemInfo
19
25
import java.lang.management.ManagementFactory
20
26
import java.lang.management.OperatingSystemMXBean
27
+ import java.net.InetAddress
21
28
import java.nio.ByteOrder
22
29
import elide.annotations.Factory
23
30
import elide.annotations.Singleton
@@ -142,6 +149,32 @@ private val STUBBED_USER: UserInfo = UserInfo.of(
142
149
STUBBED_USERINFO_GID ,
143
150
)
144
151
152
+ // Module member names.
153
+ private val moduleMembers = arrayOf(
154
+ " EOL" ,
155
+ " devNull" ,
156
+ " constants" ,
157
+ " availableParallelism" ,
158
+ " arch" ,
159
+ " cpus" ,
160
+ " endianness" ,
161
+ " freemem" ,
162
+ " getPriority" ,
163
+ " homedir" ,
164
+ " hostname" ,
165
+ " loadavg" ,
166
+ " networkInterfaces" ,
167
+ " platform" ,
168
+ " release" ,
169
+ " setPriority" ,
170
+ " tmpdir" ,
171
+ " totalmem" ,
172
+ " type" ,
173
+ " uptime" ,
174
+ " userInfo" ,
175
+ " version" ,
176
+ )
177
+
145
178
// Installs the Node OS module into the intrinsic bindings.
146
179
@Intrinsic @Factory internal class NodeOperatingSystemModule : AbstractNodeBuiltinModule () {
147
180
// Provide a compliant instance of the OS API to the DI context.
@@ -159,13 +192,47 @@ internal object NodeOperatingSystem {
159
192
/* * Primordial symbol where the OS API implementation is installed. */
160
193
internal const val SYMBOL : String = " __Elide_node_os__"
161
194
195
+ internal abstract class ModuleBase : ProxyObject , OperatingSystemAPI {
196
+ override fun getMemberKeys (): Array <String > = moduleMembers
197
+ override fun hasMember (key : String ): Boolean = key in moduleMembers
198
+ override fun getMember (key : String? ): Any? = when (key) {
199
+ " EOL" -> EOL
200
+ " devNull" -> devNull
201
+ " constants" -> constants
202
+ " availableParallelism" -> ProxyExecutable { availableParallelism() }
203
+ " arch" -> ProxyExecutable { arch() }
204
+ " cpus" -> ProxyExecutable { cpus() }
205
+ " endianness" -> ProxyExecutable { endianness() }
206
+ " freemem" -> ProxyExecutable { freemem() }
207
+ " getPriority" -> ProxyExecutable { args -> getPriority(args.getOrNull(0 )) }
208
+ " homedir" -> ProxyExecutable { homedir() }
209
+ " hostname" -> ProxyExecutable { hostname() }
210
+ " loadavg" -> ProxyExecutable { loadavg() }
211
+ " networkInterfaces" -> ProxyExecutable { networkInterfaces() }
212
+ " platform" -> ProxyExecutable { platform() }
213
+ " release" -> ProxyExecutable { release() }
214
+ " setPriority" -> ProxyExecutable { args -> setPriority(args.getOrNull(0 ), args.getOrNull(1 )) }
215
+ " tmpdir" -> ProxyExecutable { tmpdir() }
216
+ " totalmem" -> ProxyExecutable { totalmem() }
217
+ " type" -> ProxyExecutable { type() }
218
+ " uptime" -> ProxyExecutable { uptime() }
219
+ " userInfo" -> ProxyExecutable { args -> userInfo(args.getOrNull(0 )?.`as `(UserInfoOptions ::class .java)) }
220
+ " version" -> ProxyExecutable { version() }
221
+ else -> null
222
+ }
223
+
224
+ override fun putMember (key : String? , value : Value ? ) {
225
+ throw UnsupportedOperationException (" Cannot modify `os` module" )
226
+ }
227
+ }
228
+
162
229
/* *
163
230
* ## Stubbed OS
164
231
*
165
232
* Stubbed implementation of the `os` module for use in simulated or host-restricted environments; returns static,
166
233
* predetermined values for all OS-related calls. Where necessary, chooses POSIX-style values.
167
234
*/
168
- internal object StubbedOs : OperatingSystemAPI {
235
+ internal class StubbedOs : ModuleBase (), OperatingSystemAPI {
169
236
override val family: OSType get() = POSIX
170
237
override val EOL : String get() = posixEOL
171
238
override val constants: OperatingSystemConstants get() = TODO (" Not yet implemented: `os.constants`" )
@@ -175,19 +242,19 @@ internal object NodeOperatingSystem {
175
242
override fun cpus (): List <CPUInfo > = STUBBED_CPUS
176
243
override fun endianness (): String = STUBBED_ENDIANNESS
177
244
override fun freemem (): Long = STUBBED_FREEMEM
178
- override fun getPriority (pid : Long ? ): Int = STUBBED_PRIORITY
245
+ override fun getPriority (pid : Value ? ): Int = STUBBED_PRIORITY
179
246
override fun homedir (): String = STUBBED_HOMEDIR
180
247
override fun hostname (): String = STUBBED_HOSTNAME
181
248
override fun loadavg (): List <Double > = listOf (0.0 , 0.0 , 0.0 )
182
249
override fun networkInterfaces (): Map <String , List <NetworkInterfaceInfo >> = STUBBED_NICS
183
250
override fun platform (): String = STUBBED_PLATFORM
184
251
override fun release (): String = STUBBED_RELEASE
185
- override fun setPriority (pid : Long , priority : Int ) = Unit
252
+ override fun setPriority (pid : Value ? , priority : Value ? ) = Unit
186
253
override fun tmpdir (): String = STUBBED_TMPDIR
187
254
override fun totalmem (): Long = STUBBED_TOTALMEM
188
255
override fun type (): String = STUBBED_TYPE
189
256
override fun uptime (): Double = STUBBED_UPTIME
190
- override fun userInfo (options : UserInfoOptions ): UserInfo = STUBBED_USER
257
+ override fun userInfo (options : UserInfoOptions ? ): UserInfo = STUBBED_USER
191
258
override fun version (): String = STUBBED_VERSION
192
259
}
193
260
@@ -200,12 +267,22 @@ internal object NodeOperatingSystem {
200
267
* @see Win32 for Win32-style systems
201
268
* @see Posix for POSIX-style systems
202
269
*/
203
- abstract class BaseOS protected constructor (override val family : OSType ) : OperatingSystemAPI {
270
+ @ReflectiveAccess @Introspected abstract class BaseOS protected constructor (override val family : OSType ) :
271
+ ModuleBase (),
272
+ OperatingSystemAPI ,
273
+ ProxyObject {
204
274
/* * Obtain system info. */
205
275
private val systemInfo: SystemInfo by lazy { SystemInfo () }
206
276
private val osManager: OperatingSystemMXBean by lazy { ManagementFactory .getOperatingSystemMXBean() }
207
277
208
- private fun mapJvmArchToNodeArch (arch : String ): String = when (arch.lowercase().trim()) {
278
+ private fun wrapCleanup (subject : String? ): String =
279
+ subject?.trim()?.replace(Regex (" [\n\r ]" ), " " ) ? : " "
280
+
281
+ private fun trimTrailing (subject : String? ): String =
282
+ subject?.endsWith(" /" )?.let { if (it) subject.dropLast(1 ) else subject } ? : " "
283
+
284
+ @VisibleForTesting
285
+ internal fun mapJvmArchToNodeArch (arch : String ): String = when (arch.lowercase().trim()) {
209
286
JVM_X86 -> NODE_X86
210
287
JVM_X86_64 -> NODE_X64
211
288
JVM_AMD64 -> NODE_X64
@@ -214,9 +291,10 @@ internal object NodeOperatingSystem {
214
291
else -> " unknown"
215
292
}
216
293
217
- private fun mapJvmOsToNodeOs (os : String = System .getProperty("os.name")): String = when (os.lowercase().trim()) {
294
+ @VisibleForTesting
295
+ internal fun mapJvmOsToNodeOs (os : String = System .getProperty("os.name")): String = when (os.lowercase().trim()) {
218
296
" aix" -> OS_AIX
219
- " darwin " , " macos " -> OS_DARWIN
297
+ " mac os x " -> OS_DARWIN
220
298
" freebsd" -> OS_FREEBSD
221
299
" linux" -> OS_LINUX
222
300
" openbsd" -> OS_OPENBSD
@@ -225,8 +303,12 @@ internal object NodeOperatingSystem {
225
303
else -> " unknown"
226
304
}
227
305
228
- private fun wrapCleanup (subject : String? ): String {
229
- return subject?.trim()?.replace(Regex (" [\n\r ]" ), " " ) ? : " "
306
+ @VisibleForTesting
307
+ internal fun typeForOsName (osName : String? ): String = when (osName?.lowercase()?.trim()) {
308
+ " linux" -> OS_TYPE_LINUX
309
+ " mac os x" -> OS_TYPE_DARWIN
310
+ " windows" -> OS_TYPE_WINDOWS
311
+ else -> " unknown"
230
312
}
231
313
232
314
@Polyglot override fun availableParallelism (): Int = systemInfo.hardware.processor.logicalProcessorCount
@@ -252,10 +334,10 @@ internal object NodeOperatingSystem {
252
334
@Polyglot override fun freemem (): Long = Runtime .getRuntime().freeMemory()
253
335
254
336
// @TODO(sgammon): not yet implemented
255
- @Polyglot override fun getPriority (pid : Long ? ): Int = PRIORITY_NORMAL
337
+ @Polyglot override fun getPriority (pid : Value ? ): Int = PRIORITY_NORMAL
256
338
257
339
@Polyglot override fun homedir (): String = wrapCleanup(System .getProperty(" user.home" ))
258
- @Polyglot override fun hostname (): String = wrapCleanup(System .getProperty( " os.hostname " ) )
340
+ @Polyglot override fun hostname (): String = wrapCleanup(InetAddress .getLocalHost().hostName )
259
341
260
342
@Polyglot override fun loadavg (): List <Double > = osManager.systemLoadAverage.let {
261
343
listOf (it, 0.0 , 0.0 ) // @TODO: implement 5 and 15 minute averages
@@ -316,32 +398,26 @@ internal object NodeOperatingSystem {
316
398
}
317
399
318
400
@Polyglot override fun platform (): String = mapJvmOsToNodeOs()
319
-
320
401
@Polyglot override fun release (): String = systemInfo.operatingSystem.toString()
321
402
322
403
// @TODO(sgammon): not yet implemented
323
- @Polyglot override fun setPriority (pid : Long , priority : Int ) = Unit
324
- @Polyglot override fun tmpdir (): String = wrapCleanup(System .getProperty(" java.io.tmpdir" ))
404
+ @Polyglot override fun setPriority (pid : Value ? , priority : Value ? ) = Unit
405
+ @Polyglot override fun tmpdir (): String = trimTrailing( wrapCleanup(System .getProperty(" java.io.tmpdir" ) ))
325
406
@Polyglot override fun totalmem (): Long = Runtime .getRuntime().totalMemory()
326
407
327
- @Polyglot override fun type (): String = when (System .getProperty(" os.name" )) {
328
- " linux" -> OS_TYPE_LINUX
329
- " darwin" -> OS_TYPE_DARWIN
330
- " windows" -> OS_TYPE_WINDOWS
331
- else -> " unknown"
332
- }
408
+ @Polyglot override fun type (): String = typeForOsName(System .getProperty(" os.name" ))
333
409
334
410
@Polyglot override fun uptime (): Double = systemInfo.operatingSystem.systemUptime.toDouble()
335
411
336
- @Polyglot override fun userInfo (options : UserInfoOptions ): UserInfo {
412
+ @Polyglot override fun userInfo (options : UserInfoOptions ? ): UserInfo {
337
413
TODO (" Not yet implemented: `os.userInfo`" )
338
414
}
339
415
340
416
@Polyglot override fun version (): String = systemInfo.operatingSystem.versionInfo.version
341
417
}
342
418
343
419
// Implements Operating System API calls for Win32-style systems.
344
- object Win32 : BaseOS(WIN32 ), OperatingSystemAPI {
420
+ @ReflectiveAccess @Introspected object Win32 : BaseOS(WIN32 ), OperatingSystemAPI {
345
421
@get:Polyglot override val EOL : String get() = win32EOL
346
422
@get:Polyglot override val devNull: String get() = win32DevNull
347
423
@@ -350,22 +426,25 @@ internal object NodeOperatingSystem {
350
426
}
351
427
352
428
// Implements Operating System API calls for POSIX-style systems.
353
- object Posix : BaseOS(POSIX ), OperatingSystemAPI {
429
+ @ReflectiveAccess @Introspected object Posix : BaseOS(POSIX ), OperatingSystemAPI {
354
430
@get:Polyglot override val EOL : String get() = posixEOL
355
431
@get:Polyglot override val devNull: String get() = posixDevNull
356
432
357
433
override val constants: OperatingSystemConstants
358
434
get() = TODO (" Not yet implemented: `os.constants` (POSIX)" )
359
435
}
360
436
437
+ // Stubbed OS module API singleton.
438
+ private val stubbed: StubbedOs by lazy { StubbedOs () }
439
+
361
440
/* *
362
441
* ## Node Operating System API: Stubbed
363
442
*
364
443
* Creates a stubbed instance of the [OperatingSystemAPI] for use in simulated or host-restricted environments.
365
444
*
366
445
* @return A stubbed instance of the [OperatingSystemAPI].
367
446
*/
368
- @JvmStatic fun stubbed (): OperatingSystemAPI = StubbedOs
447
+ @JvmStatic fun stubbed (): OperatingSystemAPI = stubbed
369
448
370
449
/* *
371
450
* ## Node Operating System API: Create or Obtain
0 commit comments