2using System.Collections;
3using System.Collections.Generic;
4using System.Collections.Immutable;
9using IKVM.ByteCode.Decoding;
18 internal readonly
partial struct ModuleDescriptor : IComparable<ModuleDescriptor>
21 public static bool operator ==(ModuleDescriptor x, ModuleDescriptor y) => x.Equals(y);
23 public static bool operator !=(ModuleDescriptor x, ModuleDescriptor y) => x.Equals(y) ==
false;
25 public static bool operator <(ModuleDescriptor x, ModuleDescriptor y) => Compare(x, y) < 0;
27 public static bool operator >(ModuleDescriptor x, ModuleDescriptor y) => Compare(x, y) > 0;
29 public static bool operator <=(ModuleDescriptor x, ModuleDescriptor y) => Compare(x, y) <= 0;
31 public static bool operator >=(ModuleDescriptor x, ModuleDescriptor y) => Compare(x, y) >= 0;
33 public static int Compare(ModuleDescriptor x, ModuleDescriptor y) => x.CompareTo(y);
42 public static ModuleDescriptor Read(ClassFile clazz)
45 throw new ArgumentNullException(nameof(clazz));
49 if (clazz.Version < 53)
50 throw new InvalidModuleDescriptorException($
"Unsupported class file version '{clazz.Version}'.");
52 if ((clazz.AccessFlags & AccessFlag.Module) != AccessFlag.Module)
53 throw new InvalidModuleDescriptorException(
"AccessFlags should be ACC_MODULE.");
55 var clazzName = clazz.Constants.Get(clazz.This).Name;
56 if (clazzName !=
"module-info")
57 throw new InvalidModuleDescriptorException(
"Class name should be 'module-info'.");
59 if (clazz.Super.IsNotNil)
60 throw new InvalidModuleDescriptorException(
"Bad super class.");
62 if (clazz.Fields.Count > 0)
63 throw new InvalidModuleDescriptorException(
"Bad fields.");
65 if (clazz.Methods.Count > 0)
66 throw new InvalidModuleDescriptorException(
"Bad methods.");
68 if (clazz.Interfaces.Count > 0)
69 throw new InvalidModuleDescriptorException(
"Bad interfaces.");
71 var builder = ReadModuleAttribute(clazz);
72 ReadModulePackagesAttribute(clazz, builder);
73 ReadModuleMainClassAttribute(clazz, builder);
75 var descriptor = builder.Build();
78 if (descriptor.Name ==
"java.base")
81 if (descriptor.Requires.Count > 0)
82 throw new InvalidModuleDescriptorException(
"The requires table for java.base must be 0 length.");
87 if (descriptor.Requires.Any(i => i.Name ==
"java.base") ==
false)
88 throw new InvalidModuleDescriptorException(
"The requires table must have an entry for java.base.");
93 catch (ByteCodeException e)
95 throw new InvalidModuleDescriptorException(
"Unable to read module descriptor.", e);
105 static Builder ReadModuleAttribute(ClassFile clazz)
107 var attribute = clazz.Attributes.FirstOrDefault(i => i.IsNotNil && i.Name.IsNotNil && clazz.Constants.Get(i.Name).Value == AttributeName.Module);
109 throw new InvalidModuleDescriptorException($
"Attribute '{AttributeName.Module}' not found.");
111 return ReadModuleAttribute(clazz, (ModuleAttribute)attribute);
121 static Builder ReadModuleAttribute(ClassFile clazz, in ModuleAttribute attribute)
123 var moduleName = clazz.Constants.Get(attribute.Name).Name;
124 if (moduleName is not
null)
125 moduleName = DecodeModuleName(moduleName);
126 if (moduleName is
null ||
string.IsNullOrEmpty(moduleName))
127 throw new InvalidModuleDescriptorException(
"Module name not found.");
129 var modifiers =
default(ModuleFlag);
130 if ((attribute.Flags & ModuleFlag.Open) != 0)
131 modifiers |= ModuleFlag.Open;
132 if ((attribute.Flags & ModuleFlag.Synthetic) != 0)
133 modifiers |= ModuleFlag.Synthetic;
134 if ((attribute.Flags & ModuleFlag.Mandated) != 0)
135 modifiers |= ModuleFlag.Mandated;
137 var builder = CreateModule(moduleName, modifiers);
139 if (attribute.Version.IsNotNil)
140 builder = builder.Version(clazz.Constants.Get(attribute.Version).Value);
142 ReadModuleRequires(clazz, attribute, builder);
143 ReadModuleExports(clazz, attribute, builder);
144 ReadModuleOpens(clazz, attribute, builder);
145 ReadModuleUses(clazz, attribute, builder);
146 ReadModuleProvides(clazz, attribute, builder);
156 static void ReadModulePackagesAttribute(ClassFile clazz, Builder builder)
158 var attribute = clazz.Attributes.FirstOrDefault(i => i.IsNotNil && i.Name.IsNotNil && clazz.Constants.Get(i.Name).Value == AttributeName.ModulePackages);
162 ReadModulePackagesAttribute(clazz, (ModulePackagesAttribute)attribute, builder);
171 static void ReadModulePackagesAttribute(ClassFile clazz, in ModulePackagesAttribute attribute, Builder builder)
173 foreach (var package
in attribute.Packages)
175 var packageName = clazz.Constants.Get(package).Name;
176 if (packageName is not
null)
177 packageName = packageName.Replace(
'/',
'.');
178 if (packageName is
null ||
string.IsNullOrEmpty(packageName))
179 throw new InvalidModuleDescriptorException(
"Bad package name on module packages.");
181 builder.Package(packageName);
190 static void ReadModuleMainClassAttribute(ClassFile clazz, Builder builder)
192 var attribute = clazz.Attributes.FirstOrDefault(i => i.IsNotNil && i.Name.IsNotNil && clazz.Constants.Get(i.Name).Value == AttributeName.ModuleMainClass);
196 ReadModuleMainClassAttribute(clazz, (ModuleMainClassAttribute)attribute, builder);
205 static void ReadModuleMainClassAttribute(ClassFile clazz, in ModuleMainClassAttribute attribute, Builder builder)
207 var mainClassName = clazz.Constants.Get(attribute.MainClass).Name;
208 if (mainClassName is not
null)
209 mainClassName = mainClassName.Replace(
'/',
'.');
210 if (mainClassName is
null ||
string.IsNullOrEmpty(mainClassName))
211 throw new InvalidModuleDescriptorException(
"Bad main class name on module main class attribute.");
213 builder.MainClass(mainClassName);
223 static void ReadModuleRequires(ClassFile clazz, in ModuleAttribute attribute, Builder builder)
225 foreach (var
requires in attribute.Requires)
227 var moduleName = clazz.Constants.Get(
requires.
Module).Name;
228 if (moduleName is not
null)
229 moduleName = DecodeModuleName(moduleName);
230 if (moduleName is
null ||
string.IsNullOrEmpty(moduleName))
231 throw new InvalidModuleDescriptorException(
"Bad module name on requires.");
233 var modifiers =
default(ModuleRequiresFlag);
234 if ((
requires.Flag & ModuleRequiresFlag.Transitive) != 0)
235 modifiers |= ModuleRequiresFlag.Transitive;
236 if ((
requires.Flag & ModuleRequiresFlag.StaticPhase) != 0)
237 modifiers |= ModuleRequiresFlag.StaticPhase;
238 if ((
requires.Flag & ModuleRequiresFlag.Synthetic) != 0)
239 modifiers |= ModuleRequiresFlag.Synthetic;
240 if ((
requires.Flag & ModuleRequiresFlag.Mandated) != 0)
241 modifiers |= ModuleRequiresFlag.Mandated;
243 if (moduleName ==
"java.base")
245 if ((modifiers & ModuleRequiresFlag.Synthetic) != 0)
246 throw new InvalidModuleDescriptorException(
"The requires entry for java.base has ACC_SYNTHETIC set");
255 if (clazz.Version.Major >= 54)
257 var hasTransitive = (modifiers & ModuleRequiresFlag.Transitive) != 0;
258 var hasStatic = (modifiers & ModuleRequiresFlag.StaticPhase) != 0;
259 if ((hasTransitive &&
"java.se" != moduleName) || hasStatic)
260 throw new InvalidModuleDescriptorException($
"The requires entry for java.base has {(hasStatic ? "ACC_STATIC_PHASE
" : "ACC_TRANSITIVE
")} set.");
264 if (
requires.Version.IsNotNil)
266 var version = clazz.Constants.Get(
requires.Version).Value;
267 builder = builder.Requires(modifiers, moduleName, version);
271 builder = builder.Requires(modifiers, moduleName);
283 static void ReadModuleExports(ClassFile clazz, in ModuleAttribute attribute, Builder builder)
285 foreach (var exports
in attribute.Exports)
287 var packageName = clazz.Constants.Get(exports.Package).Name;
288 if (packageName is not
null)
289 packageName = packageName.Replace(
'/',
'.');
290 if (packageName is
null ||
string.IsNullOrEmpty(packageName))
291 throw new InvalidModuleDescriptorException(
"Bad package name on module exports.");
293 var modifiers =
default(ModuleExportsFlag);
294 if ((exports.Flags & ModuleExportsFlag.Synthetic) != 0)
295 modifiers |= ModuleExportsFlag.Synthetic;
296 if ((exports.Flags & ModuleExportsFlag.Mandated) != 0)
297 modifiers |= ModuleExportsFlag.Mandated;
299 builder = builder.Exports(modifiers, packageName, ToHashSet(clazz, exports.Modules));
310 static void ReadModuleOpens(ClassFile clazz, in ModuleAttribute attribute, Builder builder)
313 if (attribute.Opens.Count > 0)
314 throw new InvalidModuleDescriptorException(
"The opens table for an open module must be 0 length.");
316 foreach (var opens
in attribute.Opens)
318 var packageName = clazz.Constants.Get(opens.Package).Name;
319 if (packageName is not
null)
320 packageName = packageName.Replace(
'/',
'.');
321 if (packageName is
null ||
string.IsNullOrEmpty(packageName))
322 throw new InvalidModuleDescriptorException(
"Bad package name on module opens.");
324 var modifiers =
default(ModuleOpensFlag);
325 if ((opens.Flags & ModuleOpensFlag.Synthetic) != 0)
326 modifiers |= ModuleOpensFlag.Synthetic;
327 if ((opens.Flags & ModuleOpensFlag.Mandated) != 0)
328 modifiers |= ModuleOpensFlag.Mandated;
330 builder = builder.Opens(opens.Flags, packageName, ToHashSet(clazz, opens.Modules));
341 static void ReadModuleUses(ClassFile clazz, in ModuleAttribute attribute, Builder builder)
343 foreach (var uses
in attribute.Uses)
345 var className = clazz.Constants.Get(uses).Name;
346 if (className is not
null)
347 className = className.Replace(
'/',
'.');
348 if (className is
null ||
string.IsNullOrEmpty(className))
349 throw new InvalidModuleDescriptorException(
"Bad class name on module uses.");
351 builder = builder.Uses(className);
362 static void ReadModuleProvides(ClassFile clazz, in ModuleAttribute attribute, Builder builder)
364 foreach (var provides
in attribute.Provides)
366 var serviceName = clazz.Constants.Get(provides.Class).Name;
367 if (serviceName is not
null)
368 serviceName = serviceName.Replace(
'/',
'.');
369 if (serviceName is
null ||
string.IsNullOrEmpty(serviceName))
370 throw new InvalidModuleDescriptorException(
"Bad service name on module provides.");
372 builder = builder.Provides(serviceName, ToArray(clazz, provides.With));
382 static string DecodeModuleName(
string value)
384 if (value.Length == 0)
385 throw new InvalidModuleDescriptorException(
"Module name is zero length.");
388 while (i < value.Length)
390 int cp =
char.ConvertToUtf32(value, i);
391 if (cp ==
':' || cp ==
'@' || cp < 0x20)
392 throw new InvalidModuleDescriptorException($
"Module name has illegal character: U+{cp:X4}.");
396 return DecodeModuleName(i, value);
408 static string DecodeModuleName(
int i,
string value)
410 var sb =
new ValueStringBuilder(value.Length - i);
416 int cp =
char.ConvertToUtf32(value, j);
417 AppendUtf32(
ref sb, cp);
422 while (i < value.Length)
424 int cp =
char.ConvertToUtf32(value, i);
425 if (cp ==
':' || cp ==
'@' || cp < 0x20)
427 throw new InvalidModuleDescriptorException($
"Module name has illegal character: U+{cp:X4}.");
433 j = i + CharCount(cp);
434 if (j >= value.Length)
435 throw new InvalidModuleDescriptorException(
"Module name has illegal escape sequence.");
437 int next =
char.ConvertToUtf32(value, j);
438 if (next !=
'\\' && next !=
':' && next !=
'@')
439 throw new InvalidModuleDescriptorException(
"Module name has illegal escape sequence.");
441 AppendUtf32(
ref sb, next);
442 i += CharCount(next);
446 AppendUtf32(
ref sb, cp);
452 return sb.ToString();
460 static void AppendUtf32(
ref ValueStringBuilder builder,
int utf32)
462 var c = (Span<char>)stackalloc
char[2];
463 var l = WriteCodePoint(utf32, c);
464 builder.Append(c[..l]);
473 static int WriteCodePoint(
int utf32, Span<char> utf16)
475 if (utf32 < 0xD800 || (utf32 > 0xDFFF && utf32 < 0x10000))
477 utf16[0] = (char)utf32;
484 utf16[0] = (char)(((0b11111111110000000000 & utf32) >> 10) + 0xD800);
485 utf16[1] = (char)(((0b00000000001111111111 & utf32) >> 00) + 0xDC00);
495 static int CharCount(
int utf32)
497 return utf32 < 0xD800 || (utf32 > 0xDFFF && utf32 < 0x10000) ? 2 : 1;
506 static ImmutableHashSet<string> ToHashSet(ClassFile clazz, in ModuleConstantHandleTable modules)
508 var hs = ImmutableHashSet.CreateBuilder<
string>();
509 foreach (var i
in modules)
510 if (clazz.Constants.Get(i).Name is { } name)
511 hs.Add(DecodeModuleName(name));
513 return hs.ToImmutable();
522 static ImmutableArray<string> ToArray(ClassFile clazz, in ClassConstantHandleTable classes)
524 var ar = ImmutableArray.CreateBuilder<
string>(classes.Count);
525 foreach (var i
in classes)
526 if (clazz.Constants.Get(i).Name is { } name)
527 ar.Add(name.Replace(
'/',
'.'));
529 return ar.ToImmutable();
532 readonly
string _name;
533 readonly ModuleVersion _version;
534 readonly
bool _automatic;
535 readonly ModuleFlag _modifiers;
536 readonly ImmutableHashSet<ModuleRequires> _requires;
537 readonly ImmutableHashSet<ModuleExports> _exports;
538 readonly ImmutableHashSet<ModuleOpens> _opens;
539 readonly ImmutableHashSet<string> _uses;
540 readonly ImmutableHashSet<ModuleProvides> _provides;
541 readonly ImmutableHashSet<string> _packages;
542 readonly
string? _mainClass;
558 internal ModuleDescriptor(
560 ModuleVersion version,
562 ModuleFlag modifiers,
563 ImmutableHashSet<ModuleRequires>
requires,
564 ImmutableHashSet<ModuleExports> exports,
565 ImmutableHashSet<ModuleOpens> opens,
566 ImmutableHashSet<string> uses,
567 ImmutableHashSet<ModuleProvides> provides,
568 ImmutableHashSet<string> packages,
573 _automatic = automatic;
574 _modifiers = modifiers;
575 _requires =
requires;
579 _provides = provides;
580 _packages = packages;
581 _mainClass = mainClass;
587 public readonly
string Name => _name;
592 public readonly ModuleVersion Version => _version;
597 public readonly ModuleFlag Modifiers => _modifiers;
602 public readonly
bool IsOpen => (
Modifiers & ModuleFlag.Open) != 0;
607 public readonly
bool IsAutomatic => _automatic;
612 public readonly ImmutableHashSet<ModuleRequires> Requires => _requires;
617 public readonly ImmutableHashSet<ModuleExports> Exports => _exports;
622 public readonly ImmutableHashSet<ModuleOpens> Opens => _opens;
627 public readonly ImmutableHashSet<string> Uses => _uses;
632 public readonly ImmutableHashSet<ModuleProvides> Provides => _provides;
640 public readonly ImmutableHashSet<string> Packages => _packages;
645 public readonly
string? MainClass => _mainClass;
663 public readonly
int CompareTo(ModuleDescriptor other)
665 int c = _name.CompareTo(other._name);
669 c = _version.CompareTo(other._version);
673 c = _modifiers.CompareTo(other._modifiers);
677 c = CompareTo(_requires, other._requires);
681 c = CompareTo(_packages, other._packages);
685 c = CompareTo(_exports, other._exports);
689 c = CompareTo(_opens, other._opens);
693 c = CompareTo(_uses, other._uses);
697 c = CompareTo(_provides, other._provides);
701 c = Comparer.Default.Compare(_mainClass, other._mainClass);
715 static int CompareTo<T>(ImmutableHashSet<T> s1, ImmutableHashSet<T> s2)
717 var a1 = s1.ToArray();
718 var a2 = s2.ToArray();
725 public readonly
override bool Equals(
object? obj)
727 return obj is ModuleDescriptor other && Equals(other);
735 public readonly
bool Equals(in ModuleDescriptor other)
738 _name.Equals(other._name) &&
739 _version.Equals(other._version) &&
740 _modifiers.Equals(other._modifiers) &&
741 _requires.SetEquals(other._requires) &&
742 _exports.SetEquals(other._exports) &&
743 _opens.SetEquals(other._opens) &&
744 _uses.SetEquals(other._uses) &&
745 _provides.SetEquals(other._provides) &&
746 _packages.SetEquals(other._packages) &&
747 _mainClass == other._mainClass;
751 public readonly
override int GetHashCode()
753 var hc =
new HashCode();
757 hc.AddRange(_requires);
758 hc.AddRange(_exports);
761 hc.AddRange(_provides);
762 hc.AddRange(_packages);
764 return hc.ToHashCode();
768 public override string? ToString()
770 return _version.IsValid ? _name +
" (@" + _version +
")" : _name;
IKVM.Reflection.Module Module
global::java.lang.invoke.LambdaForm.Name Name
Implements an IComparer<T> which lexicographically compares a two lists.
override int Compare(TList? x, TList? y)
static new readonly LexicographicListComparer< T > Default
Returns a default LexicographicListComparer<T> instance.