IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
Launcher.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.IO;
5using System.Linq;
6using System.Net;
7using System.Net.Sockets;
8using System.Reflection;
9using System.Runtime.ExceptionServices;
10using System.Text;
11using System.Text.Json;
12using System.Text.Json.Serialization;
13using System.Threading;
14
15using IKVM.Attributes;
20
21namespace IKVM.Runtime
22{
23
28 public static class Launcher
29 {
30
31#if FIRST_PASS == false && IMPORTER == false && EXPORTER == false
32
33 static CallerIDAccessor callerIDAccessor;
34 static ClassAccessor classAccessor;
35 static MethodAccessor methodAccessor;
36 static SystemAccessor systemAccessor;
37 static ThreadAccessor threadAccessor;
38 static ThreadGroupAccessor threadGroupAccessor;
39 static LauncherHelperAccessor launcherHelperAccessor;
40
41 static CallerIDAccessor CallerIDAccessor => JVM.Internal.BaseAccessors.Get(ref callerIDAccessor);
42
43 static ClassAccessor ClassAccessor => JVM.Internal.BaseAccessors.Get(ref classAccessor);
44
45 static MethodAccessor MethodAccessor => JVM.Internal.BaseAccessors.Get(ref methodAccessor);
46
47 static SystemAccessor SystemAccessor => JVM.Internal.BaseAccessors.Get(ref systemAccessor);
48
49 static ThreadAccessor ThreadAccessor => JVM.Internal.BaseAccessors.Get(ref threadAccessor);
50
51 static ThreadGroupAccessor ThreadGroupAccessor => JVM.Internal.BaseAccessors.Get(ref threadGroupAccessor);
52
53 static LauncherHelperAccessor LauncherHelperAccessor => JVM.Internal.BaseAccessors.Get(ref launcherHelperAccessor);
54
55#endif
56
60 class IkvmStartEvent
61 {
62
63 [JsonPropertyName("processId")]
64 public int ProcessId { get; set; }
65
66 }
67
74 static IEnumerable<string> Prepend(IEnumerable<string> source, string value)
75 {
76 yield return value;
77 foreach (var i in source)
78 yield return i;
79 }
80
86 static string[] Glob(string path)
87 {
88 try
89 {
90 var dir = Path.GetDirectoryName(path);
91 if (dir == "")
92 dir = null;
93
94 var list = new List<string>();
95 foreach (var fsi in new DirectoryInfo(dir ?? Environment.CurrentDirectory).GetFileSystemInfos(Path.GetFileName(path)))
96 list.Add(dir != null ? Path.Combine(dir, fsi.Name) : fsi.Name);
97
98 if (list.Count == 0)
99 return [path];
100
101 return list.ToArray();
102 }
103 catch
104 {
105 return [path];
106 }
107 }
108
114 static string[] Glob(string[] paths)
115 {
116 var list = new List<string>();
117 for (var i = 0; i < paths.Length; i++)
118 {
119 var path = paths[i];
120 if (path.Contains('*') || path.Contains('?'))
121 list.AddRange(Glob(path));
122 else
123 list.Add(path);
124 }
125
126 return list.ToArray();
127 }
128
133 static void SetUserProperties(IDictionary<string, string> properties)
134 {
135#if FIRST_PASS || IMPORTER
136 throw new NotImplementedException();
137#else
138 if (properties is null)
139 throw new ArgumentNullException(nameof(properties));
140
141 foreach (var kvp in properties)
142 JVM.Properties.User[kvp.Key] = kvp.Value;
143#endif
144 }
145
149 static void EnterMainThread()
150 {
151#if FIRST_PASS || IMPORTER
152 throw new NotImplementedException();
153#else
154 if (Thread.CurrentThread.Name == null)
155 {
156 try
157 {
158 Thread.CurrentThread.Name = "main";
159 }
160 catch (InvalidOperationException)
161 {
162
163 }
164 }
165
166 // first invocation of a type in the base assembly
167 ThreadAccessor.InvokeCurrentThread();
168
169 try
170 {
172 sun.misc.Signal.handle(new sun.misc.Signal("BREAK"), sun.misc.SignalHandler.SIG_DFL);
173 }
174 catch (java.lang.IllegalArgumentException)
175 {
176 // ignore
177 }
178#endif
179 }
180
184 static void ExitMainThread()
185 {
186#if FIRST_PASS || IMPORTER
187 throw new NotImplementedException();
188#else
189 // FXBUG when the main thread ends, it doesn't actually die, it stays around to manage the lifetime
190 // of the CLR, but in doing so it also keeps alive the thread local storage for this thread and we
191 // use the TLS as a hack to track when the thread dies (if the object stored in the TLS is finalized,
192 // we know the thread is dead). So to make that work for the main thread, we use jniDetach which
193 // explicitly cleans up our thread.
194 ThreadAccessor.InvokeDie(ThreadAccessor.InvokeCurrentThread());
195#endif
196 }
197
202 static string GetVersionAndCopyrightInfo()
203 {
204 var assembly = typeof(Launcher).Assembly;
205 var copyright = assembly.GetCustomAttributes<AssemblyCopyrightAttribute>().FirstOrDefault();
206 if (copyright is not null)
207 return $"IKVM version {assembly.GetName().Version}{Environment.NewLine}{copyright.Copyright}";
208
209 return "";
210 }
211
215 static void PrintVersion()
216 {
217#if FIRST_PASS || IMPORTER
218 throw new NotImplementedException();
219#else
220 Console.WriteLine(GetVersionAndCopyrightInfo());
221 Console.WriteLine("CLR version: {0} ({1} bit)", Environment.Version, IntPtr.Size * 8);
222 var ver = SystemAccessor.InvokeGetProperty("openjdk.version");
223 if (ver != null)
224 Console.WriteLine("OpenJDK version: {0}", ver);
225#endif
226 }
227
232 static void AddBootClassPathAssembly(Assembly assembly)
233 {
234#if FIRST_PASS || IMPORTER
235 throw new NotImplementedException();
236#else
237 JVM.Context.ClassLoaderFactory.GetBootstrapClassLoader().AddDelegate(JVM.Context.AssemblyClassLoaderFactory.FromAssembly(assembly));
238#endif
239 }
240
247 static bool ArgEquals(ReadOnlySpan<char> a, string b)
248 {
249 return a.Equals(b.AsSpan(), StringComparison.Ordinal);
250 }
251
262 [HideFromJava(HideFromJavaFlags.StackTrace)]
263 public static int Run(Assembly assembly, string main, bool jar, string[] args, string rarg, IDictionary<string, string> properties)
264 {
265 if (args is null)
266 throw new ArgumentNullException(nameof(args));
267
268#if FIRST_PASS || IMPORTER
269 throw new NotImplementedException();
270#else
271 HandleDebugTrace();
272
273 // initialize attribute parsing
274 var initialize = properties != null ? new Dictionary<string, string>(properties) : new Dictionary<string, string>();
275 var showversion = false;
276 var exit = false;
277 var hasMainArg = false;
278 var jvmArgs = args.Where(i => rarg != null).Where(i => i.StartsWith(rarg)).Select(i => i.Substring(rarg.Length)).ToList();
279 var appArgs = args.Where(i => rarg == null || i.StartsWith(rarg) == false).ToList();
280 var appArgsReset = false;
281
282 // classpath from environment by default
283 initialize["java.class.path"] = ".";
284 if (Environment.GetEnvironmentVariable("CLASSPATH") is string cp && !string.IsNullOrEmpty(cp))
285 initialize["java.class.path"] = string.Join(Path.PathSeparator.ToString(), Glob(cp.Split(Path.PathSeparator)));
286
287 // ikvm.home.root from environment by default
288 if (Environment.GetEnvironmentVariable("IKVM_HOME_ROOT") is string ihr && !string.IsNullOrEmpty(ihr))
289 initialize["ikvm.home.root"] = ihr;
290
291 // ikvm.home from environment by default
292 if (Environment.GetEnvironmentVariable("IKVM_HOME") is string ih && !string.IsNullOrEmpty(ih))
293 initialize["ikvm.home"] = ih;
294
295 // process through each incoming argument
296 for (var jvmArg = jvmArgs.GetEnumerator(); jvmArg.MoveNext();)
297 {
298 var arg = jvmArg.Current.AsSpan();
299 if (hasMainArg == false && arg.StartsWith("-".AsSpan()))
300 {
301 // define system property
302 if (arg.StartsWith("-D".AsSpan()))
303 {
304 var def = arg.Slice(2);
305 var sep = def.IndexOf('=');
306 var key = sep > -1 ? def.Slice(0, sep) : def;
307 var val = sep > -1 ? def.Slice(sep + 1) : "".AsSpan();
308 initialize[key.ToString()] = val.ToString();
309 continue;
310 }
311
312 if (ArgEquals(arg, "-ea") || ArgEquals(arg, "-enableassertions"))
313 {
315 continue;
316 }
317
318 if (arg.StartsWith("-ea:".AsSpan()) || arg.StartsWith("-enableassertions:".AsSpan()))
319 {
320 if (arg.IndexOf(':') is int v && v > -1)
321 Assertions.EnableAssertions(arg.Slice(v + 1).ToString());
322 }
323
324 if (ArgEquals(arg, "-da") || ArgEquals(arg, "-disableassertions"))
325 {
327 continue;
328 }
329
330 if (arg.StartsWith("-da:".AsSpan()) || arg.StartsWith("-disableassertions:".AsSpan()))
331 {
332 if (arg.IndexOf(':') is int v && v > -1)
333 Assertions.DisableAssertions(arg.Slice(v + 1).ToString());
334 }
335
336 if (ArgEquals(arg, "-esa") || ArgEquals(arg, "-enablesystemassertions"))
337 {
339 continue;
340 }
341
342 if (ArgEquals(arg, "-dsa") || ArgEquals(arg, "-disablesystemassertions"))
343 {
345 continue;
346 }
347
348 if (ArgEquals(arg, "-cp") || ArgEquals(arg, "-classpath"))
349 {
350 if (jvmArg.MoveNext() == false)
351 {
352 Console.Error.WriteLine("Error: {0} requires class path specification", arg.ToString());
353 PrintHelp();
354 return 1;
355 }
356
357 initialize["java.class.path"] = string.Join(Path.PathSeparator.ToString(), Glob(jvmArg.Current.Split(Path.PathSeparator)));
358 continue;
359 }
360
361 if (ArgEquals(arg, "-version"))
362 {
363 showversion = true;
364 exit = true;
365 continue;
366 }
367
368 if (ArgEquals(arg, "-showversion"))
369 {
370 showversion = true;
371 continue;
372 }
373
374 if (ArgEquals(arg, "-jar"))
375 {
376 jar = true;
377 continue;
378 }
379
380 if (ArgEquals(arg, "-?") || ArgEquals(arg, "-help"))
381 {
382 PrintHelp();
383 return 1;
384 }
385
386 if (ArgEquals(arg, "-X"))
387 {
388 PrintXHelp();
389 return 1;
390 }
391
392 if (ArgEquals(arg, "-Xtime"))
393 {
394 Console.Error.WriteLine("Unrecognized option: {0}", arg.ToString());
395 return 1;
396 }
397
398 if (ArgEquals(arg, "-Xbreak"))
399 {
400 Debugger.Break();
401 continue;
402 }
403
404 if (ArgEquals(arg, "-Xverify"))
405 {
406 JVM.RelaxedVerification = false;
407 continue;
408 }
409
410 if (arg.StartsWith("-Xreference:".AsSpan()))
411 {
412 if (arg.IndexOf(':') is int v && v > -1)
413 AddBootClassPathAssembly(Assembly.LoadFrom(arg.Slice(v + 1).ToString()));
414
415 continue;
416 }
417
418 if (ArgEquals(arg, "-XX:+AllowNonVirtualCalls"))
419 {
420 JVM.AllowNonVirtualCalls = true;
421 continue;
422 }
423
424 if (arg.StartsWith("-Xms".AsSpan()) ||
425 arg.StartsWith("-Xmx".AsSpan()) ||
426 arg.StartsWith("-Xmn".AsSpan()) ||
427 arg.StartsWith("-Xss".AsSpan()) ||
428 arg.StartsWith("-XX:".AsSpan()) ||
429 arg.StartsWith("-mn".AsSpan()) ||
430 arg.StartsWith("-ms".AsSpan()) ||
431 arg.StartsWith("-mx".AsSpan()) ||
432 ArgEquals(arg, "-Xmixed") ||
433 ArgEquals(arg, "-Xint") ||
434 ArgEquals(arg, "-Xincgc") ||
435 ArgEquals(arg, "-Xbatch") ||
436 ArgEquals(arg, "-Xfuture") ||
437 ArgEquals(arg, "-Xrs") ||
438 ArgEquals(arg, "-Xcheck:jni") ||
439 ArgEquals(arg, "-Xshare:off") ||
440 ArgEquals(arg, "-Xshare:auto") ||
441 ArgEquals(arg, "-Xshare:on"))
442 {
443 Console.Error.WriteLine("Ignoring unrecognized option: {0}", arg.ToString());
444 continue;
445 }
446
447 Console.Error.WriteLine("Unrecognized option: {0}", arg.ToString());
448 return 1;
449 }
450 else if (hasMainArg == false)
451 {
452 hasMainArg = true;
453 main = arg.ToString();
454 continue;
455 }
456 else
457 {
458 // indicate we're resetting the application arguments
459 if (appArgsReset == false)
460 {
461 appArgsReset = true;
462 appArgs.Clear();
463 }
464
465 // append new application argument
466 appArgs.Add(arg.ToString());
467 continue;
468 }
469 }
470
471 try
472 {
473 // if a jar file is specified, we're going to set the classpath to the jar itself
474 if (jar)
475 initialize["java.class.path"] = main;
476
477 // like the JDK we don't quote the args (even if they contain spaces)
478 initialize["sun.java.command"] = string.Join(" ", Prepend(appArgs, main));
479 initialize["sun.java.launcher"] = "SUN_STANDARD";
480
481 // apply the loaded VM properties
482 SetUserProperties(initialize);
483
484 // VM initialization, configures system properties, done before any static initializers
485 JVM.Init();
486
487 // ensure the entry assembly is added to the classpath
488 // we do this after Init since it triggers the VFS
489 if (assembly != null)
490 AddBootClassPathAssembly(assembly);
491
492 // first entry into base assembly
493 EnterMainThread();
494
495 // we were instructed to show the version
496 if (showversion)
497 PrintVersion();
498
499 // we were instructed to exit immediately
500 if (exit)
501 return 0;
502
503 // we require a main argument, either a class name or jar file
504 if (main == null)
505 {
506 PrintHelp();
507 return 1;
508 }
509
510 // process the main argument, returning the true value, and resetting the command property to match
511 var clazz = LauncherHelperAccessor.InvokeCheckAndLoadMain(true, jar ? 2 : 1, main);
512 SystemAccessor.InvokeSetProperty("sun.java.command", initialize["sun.java.command"]);
513
514 // find the main method and ensure it is accessible
515 var method = ClassAccessor.InvokeGetMethod(clazz, "main", ClassAccessor.InitArray(JVM.Context.ClassLoaderFactory.GetJavaTypeFromType(typeof(string[])).ClassObject), CallerIDAccessor.InvokeCreate(typeof(Launcher).TypeHandle));
516 MethodAccessor.InvokeSetAccessible(method, true);
517
518 try
519 {
520 // invoke main method, which is responsible for exit
521 MethodAccessor.InvokeInvoke(method, null, new[] { appArgs.ToArray() });
522 return 0;
523 }
524 catch (java.lang.reflect.InvocationTargetException e)
525 {
526 // we want to unwrap the cause to report to the user
527 ExceptionDispatchInfo.Capture(e.getCause()).Throw();
528 throw null;
529 }
530 }
531 catch (Exception e)
532 {
533 var thread = ThreadAccessor.InvokeCurrentThread();
534 ThreadGroupAccessor.InvokeUncaughtException(ThreadAccessor.InvokeGetThreadGroup(thread), thread, ikvm.runtime.Util.mapException(e));
535 }
536 finally
537 {
538 ExitMainThread();
539 }
540
541 return 1;
542#endif
543 }
544
548 static void HandleDebugTrace()
549 {
550 var debugWait = 0;
551 var debugUri = (Uri)null;
552
553 // wait some number of seconds for a debugger to attach
554 if (Environment.GetEnvironmentVariable("IKVM_DEBUG_WAIT") is string debugWait_)
555 if (int.TryParse(debugWait_, out var i))
556 debugWait = i;
557
558 // send a ping message to the given hostname and port to signal a debugger to attach
559 if (Environment.GetEnvironmentVariable("IKVM_DEBUG_URI") is string debugUri_)
560 if (Uri.TryCreate(debugUri_, UriKind.Absolute, out var u))
561 debugUri = u;
562
563 // send a start event to the host
564 if (debugUri != null)
565 {
566 if (debugUri.Scheme == "tcp" && IPAddress.TryParse(debugUri.Host, out var ip) && debugUri.Port > 0)
567 {
568 var message = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new IkvmStartEvent() { ProcessId = Process.GetCurrentProcess().Id, }));
569 using var c = new TcpClient();
570 c.Connect(new IPEndPoint(ip, debugUri.Port));
571 c.GetStream().Write(message, 0, message.Length);
572 c.GetStream().WriteByte(0);
573 c.GetStream().Flush();
574 c.Close();
575 }
576 else
577 {
578 Console.Error.WriteLine("Invalid debug URI: {0}", debugUri);
579 }
580 }
581
582 // wait for debugger to attach
583 if (debugWait > 0)
584 {
585 Console.Write("Waiting for debugger...");
586
587 // waits for the debugger to be attached
588 var cts = new CancellationTokenSource(TimeSpan.FromSeconds(debugWait));
589 while (Debugger.IsAttached == false && cts.IsCancellationRequested == false)
590 {
591 Thread.Sleep(1000);
592 Console.Write(".");
593 }
594
595 Console.WriteLine();
596
597 // not attached, and cancelled?
598 if (Debugger.IsAttached == false && cts.IsCancellationRequested)
599 Console.Error.WriteLine("Debugger wait timed out.");
600 }
601 }
602
606 static void PrintHelp()
607 {
608 var exe = Process.GetCurrentProcess().ProcessName;
609 Console.Error.WriteLine("Usage: {0} [-options] class [args...]", exe);
610 Console.Error.WriteLine(" (to execute a class)");
611 Console.Error.WriteLine(" or {0} [-options] -jar jarfile [args...]", exe);
612 Console.Error.WriteLine(" (to execute a jar file)");
613 Console.Error.WriteLine();
614 Console.Error.WriteLine("where options include:");
615 Console.Error.WriteLine(" -cp <class search path of directories and zip/jar files>");
616 Console.Error.WriteLine(" -classpath <class search path of directories and zip/jar files>");
617 Console.Error.WriteLine(" A {0} separated list of directories, JAR archives,", System.IO.Path.PathSeparator);
618 Console.Error.WriteLine(" and ZIP archives to search for class files.");
619 Console.Error.WriteLine(" -D<name>=<value>");
620 Console.Error.WriteLine(" set a system property");
621 Console.Error.WriteLine(" -version print product version and exit");
622 Console.Error.WriteLine(" -showversion print product version and continue");
623 Console.Error.WriteLine(" -? -help Display this message");
624 Console.Error.WriteLine(" -X Display non-standard options");
625 Console.Error.WriteLine(" -ea[:<packagename>...|:<classname>]");
626 Console.Error.WriteLine(" -enableassertions[:<packagename>...|:<classname>]");
627 Console.Error.WriteLine(" enable assertions with specified granularity");
628 Console.Error.WriteLine(" -da[:<packagename>...|:<classname>]");
629 Console.Error.WriteLine(" -disableassertions[:<packagename>...|:<classname>]");
630 Console.Error.WriteLine(" disable assertions with specified granularity");
631 Console.Error.WriteLine(" -esa | -enablesystemassertions");
632 Console.Error.WriteLine(" enable system assertions");
633 Console.Error.WriteLine(" -dsa | -enablesystemassertions");
634 Console.Error.WriteLine(" disable system assertions");
635 Console.Error.WriteLine(" -agentlib:<libname>[=<options>]");
636 Console.Error.WriteLine(" load native agent library <libname>, e.g. -agentlib:hprof");
637 Console.Error.WriteLine(" see also, -agentlib:jdwp=help and -agentlib:hprof=help");
638 Console.Error.WriteLine(" -agentpath:<pathname>[=<options>]");
639 Console.Error.WriteLine(" load native agent library by full pathname");
640 Console.Error.WriteLine(" -javaagent:<jarpath>[=<options>]");
641 Console.Error.WriteLine(" load Java programming language agent, see java.lang.instrument");
642 Console.Error.WriteLine(" -splash:<imagepath>");
643 Console.Error.WriteLine(" show splash screen with specified image");
644 Console.Error.WriteLine("See http://www.oracle.com/technetwork/java/javase/documentation/index.html for more details.");
645 Console.Error.WriteLine("");
646 }
647
651 static void PrintXHelp()
652 {
653 Console.Error.WriteLine(" -Xnoclassgc disable class garbage collection");
654 Console.Error.WriteLine(" -Xtime time the execution");
655 Console.Error.WriteLine(" -Xwait keep process hanging around after exit");
656 Console.Error.WriteLine(" -Xbreak trigger a user defined breakpoint at startup");
657 Console.Error.WriteLine(" -Xnoglobbing Disable argument globbing");
658 Console.Error.WriteLine(" -Xverify Enable strict class file verification");
659 Console.Error.WriteLine();
660 Console.Error.WriteLine("The -X options are non-standard and subject to change without notice.");
661 Console.Error.WriteLine();
662 }
663
664 }
665
666}
IKVM.Reflection.Assembly Assembly
static void EnableSystemAssertions()
Definition Assertions.cs:91
static void DisableAssertions(string classOrPackage)
Definition Assertions.cs:76
static void EnableAssertions(string classOrPackage)
Definition Assertions.cs:71
static void DisableSystemAssertions()
Definition Assertions.cs:96
Property values loaded into the JVM from various sources.
static IDictionary< string, string > User
Gets the set of properties that are set by the user before initialization. Modification of values in ...
Main state of the running JVM.
Utility for launching a Java class from a main entry point. Parses JVM command line options,...
Definition Launcher.cs:29
static int Run(Assembly assembly, string main, bool jar, string[] args, string rarg, IDictionary< string, string > properties)
Services as the managed entry point jump for a Java executable.
Definition Launcher.cs:263
RuntimeAssemblyClassLoader FromAssembly(Assembly assembly)
Obtains the RuntimeAssemblyClassLoader for the given Assembly. This method should not be used with dy...
RuntimeAssemblyClassLoaderFactory AssemblyClassLoaderFactory
Gets the RuntimeAssemblyClassLoaderFactory associated with this instance of the runtime.
RuntimeClassLoaderFactory ClassLoaderFactory
Gets the RuntimeClassLoaderFactory associated with this instance of the runtime.
Maintains various information about the current runtime environment.
static bool IsWindows
Returns true if the current platform is Windows.