IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
StubGenerator.cs
Go to the documentation of this file.
1using System;
2using System.IO;
3using System.Collections.Generic;
4
5using IKVM.ByteCode.Encoding;
7using IKVM.ByteCode;
8using IKVM.ByteCode.Decoding;
9using IKVM.ByteCode.Buffers;
10
11
12#if EXPORTER
13using IKVM.Reflection;
14
15using Type = IKVM.Reflection.Type;
16#else
17using System.Reflection;
18#endif
19
21{
22
24 {
25
26 readonly RuntimeContext context;
27
33 {
34 this.context = context ?? throw new ArgumentNullException(nameof(context));
35 }
36
47 internal void Write(Stream stream, RuntimeJavaType type, bool includeNonPublicTypes, bool includeNonPublicInterfaces, bool includeNonPublicMembers, bool includeParameterNames, bool includeSerialVersionUID)
48 {
49 var name = type.Name.Replace('.', '/');
50
51 string super = null;
52 if (type.IsInterface)
53 super = "java/lang/Object";
54 else if (type.BaseTypeWrapper != null)
55 super = type.BaseTypeWrapper.Name.Replace('.', '/');
56
57 var builder = new ClassFileBuilder(new ClassFormatVersion(includeParameterNames ? (ushort)52 : (ushort)49, 0), (AccessFlag)type.Modifiers, name, super);
58
59 AddInterfaces(builder, type, includeNonPublicInterfaces);
60 AddMethods(builder, type, includeNonPublicMembers, includeParameterNames);
61 AddFields(builder, type, includeNonPublicMembers, includeSerialVersionUID);
62 AddInnerClassesAttribute(builder, type, name, includeNonPublicTypes);
63 AddSignatureAttribute(builder, type);
64 AddAssemblyAttribute(builder, type);
65 AddDeprecatedAttribute(builder, type);
66 AddRuntimeVisibleAnnotationsAttribute(builder, type);
67 AddRuntimeVisibleTypeAnnotationsAttribute(builder, type);
68
69 // serialize final class file
70 var blob = new BlobBuilder();
71 builder.Serialize(blob);
72 blob.WriteContentTo(stream);
73 }
74
81 void AddInterfaces(ClassFileBuilder builder, RuntimeJavaType type, bool includeNonPublicInterfaces)
82 {
83 foreach (var iface in type.Interfaces)
84 if (iface.IsPublic || includeNonPublicInterfaces)
85 builder.AddInterface(iface.Name.Replace('.', '/'));
86 }
87
95 void AddMethods(ClassFileBuilder builder, RuntimeJavaType type, bool includeNonPublicMembers, bool includeParameterNames)
96 {
97 foreach (var method in type.GetMethods())
98 {
99 var accessFlags = (AccessFlag)method.Modifiers;
100 var attributes = new AttributeTableBuilder(builder.Constants);
101
102 if (method.IsHideFromReflection == false && (method.IsPublic || method.IsProtected || includeNonPublicMembers))
103 {
104 // HACK javac has a bug in com.sun.tools.javac.code.Types.isSignaturePolymorphic() where it assumes that
105 // MethodHandle doesn't have any native methods with an empty argument list
106 // (or at least it throws a NPE when it examines the signature of a method without any parameters when it
107 // accesses argtypes.tail.tail)
108 if (method.Name == "<init>" || (type == context.JavaBase.TypeOfJavaLangInvokeMethodHandle && (method.Modifiers & Modifiers.Native) == 0))
109 {
110 // generate the method code which throws a UnsatisfiedLinkError.
111 var codeBlob = new BlobBuilder();
112 new CodeBuilder(codeBlob)
113 .New(builder.Constants.GetOrAddClass("java/lang/UnsatisfiedLinkError"))
114 .Dup()
115 .LoadConstant(builder.Constants.GetOrAddString("IKVM stubs can only be used on IKVM"))
116 .InvokeSpecial(builder.Constants.GetOrAddMethodref("java/lang/UnsatisfiedLinkError", "<init>", "(Ljava/lang/String;)V"))
117 .Athrow();
118
119 attributes.Code(3, (ushort)(method.GetParameters().Length * 2 + 1), codeBlob, e => { }, new AttributeTableBuilder(builder.Constants));
120 }
121 else
122 {
123 if ((accessFlags & AccessFlag.Abstract) == 0)
124 accessFlags |= AccessFlag.Native;
125
126 if (method.IsOptionalAttributeAnnotationValue)
127 attributes.AnnotationDefault(e => EncodeAnnotationDefault(builder, ref e, method.ReturnType));
128 }
129
130 var methodBase = method.GetMethod();
131 if (methodBase != null)
132 {
133 AddExceptionsAttribute(builder, type, method, attributes, methodBase);
134 AddDeprecatedAttribute(builder, type, method, attributes, methodBase);
135 AddAnnotationDefaultAttribute(builder, type, method, attributes, methodBase);
136 AddMethodParameters(builder, type, method, attributes, includeParameterNames);
137 }
138
139 AddSignatureAttribute(builder, type, method, attributes);
140 AddRuntimeVisibleAnnotationsAttribute(builder, type, method, attributes);
141 AddRuntimeVisibleTypeAnnotationsAttribute(builder, type, method, attributes);
142 AddRuntimeVisibleParameterAnnotationsAttribute(builder, type, method, attributes);
143
144 builder.AddMethod(accessFlags, method.Name, method.Signature.Replace('.', '/'), attributes);
145 }
146 }
147 }
148
156 void AddFields(ClassFileBuilder builder, RuntimeJavaType type, bool includeNonPublicMembers, bool includeSerialVersionUID)
157 {
158 var hasSerialVersionUID = false;
159
160 foreach (var field in type.GetFields())
161 {
162 if (field.IsHideFromReflection == false)
163 {
164 var attributes = new AttributeTableBuilder(builder.Constants);
165
166 var isSerialVersionUID = includeSerialVersionUID && field.IsSerialVersionUID;
167 hasSerialVersionUID |= isSerialVersionUID;
168
169 if (field.IsPublic || field.IsProtected || isSerialVersionUID || includeNonPublicMembers)
170 {
171 AddConstantValueAttribute(builder, type, field, attributes);
172 AddSignatureAttribute(builder, type, field, attributes);
173 AddDeprecatedAttribute(builder, type, field, attributes);
174 AddRuntimeVisibleAnnotationsAttribute(builder, type, field, attributes);
175 AddRuntimeVisibleTypeAnnotationsAttribute(builder, type, field, attributes);
176 builder.AddField((AccessFlag)field.Modifiers, field.Name, field.Signature.Replace('.', '/'), attributes);
177 }
178 }
179 }
180
181 // class is serializable but doesn't have an explicit serialVersionUID, so we add the field to record
182 // the serialVersionUID as we see it (mainly to make the Japi reports more realistic)
183 if (includeSerialVersionUID && hasSerialVersionUID == false && IsSerializable(type))
184 {
185 var fieldAttributes = new AttributeTableBuilder(builder.Constants);
186 fieldAttributes.ConstantValue(SerialVersionUID.Compute(type));
187 builder.AddField(AccessFlag.Private | AccessFlag.Static | AccessFlag.Final, "serialVersionUID", "J", fieldAttributes);
188 }
189 }
190
196 void AddRuntimeVisibleTypeAnnotationsAttribute(ClassFileBuilder builder, RuntimeJavaType type)
197 {
198 var blob = new BlobBuilder();
199 var encoder = new TypeAnnotationTableEncoder(blob);
200 if (ImportTypeAnnotations(builder, ref encoder, type, type.GetRawTypeAnnotations()))
201 builder.Attributes.Attribute(AttributeName.RuntimeVisibleTypeAnnotations, blob);
202 }
203
209 void AddRuntimeVisibleAnnotationsAttribute(ClassFileBuilder builder, RuntimeJavaType type)
210 {
211 var blob = new BlobBuilder();
212 var encoder = new AnnotationTableEncoder(blob);
213 if (EncodeAnnotations(builder, ref encoder, type.TypeAsBaseType) ||
214 EncodeAnnotationsForType(builder, ref encoder, type))
215 builder.Attributes.Attribute(AttributeName.RuntimeVisibleAnnotations, blob);
216 }
217
223 void AddDeprecatedAttribute(ClassFileBuilder builder, RuntimeJavaType type)
224 {
225 if (type.TypeAsBaseType.IsDefined(context.Resolver.ResolveCoreType(typeof(ObsoleteAttribute).FullName).AsReflection(), false))
226 builder.Attributes.Deprecated();
227 }
228
236 void AddInnerClassesAttribute(ClassFileBuilder builder, RuntimeJavaType javaType, string name, bool includeNonPublicTypes)
237 {
238 var any = false;
239 var blob = new BlobBuilder();
240 var encoder = new InnerClassTableEncoder(blob);
241
242 if (javaType.DeclaringTypeWrapper != null)
243 {
244 var innerName = name;
245 int idx = name.LastIndexOf('$');
246 if (idx >= 0)
247 innerName = innerName.Substring(idx + 1);
248
249 encoder.InnerClass(
250 builder.Constants.GetOrAddClass(name),
251 builder.Constants.GetOrAddClass(javaType.DeclaringTypeWrapper.Name.Replace('.', '/')),
252 builder.Constants.GetOrAddUtf8(innerName),
253 (AccessFlag)javaType.ReflectiveModifiers);
254
255 any = true;
256 }
257
258 foreach (var innerType in javaType.InnerClasses)
259 {
260 if (innerType.IsPublic || includeNonPublicTypes)
261 {
262 var namePart = innerType.Name;
263 namePart = namePart.Substring(namePart.LastIndexOf('$') + 1);
264
265 encoder.InnerClass(
266 builder.Constants.GetOrAddClass(innerType.Name.Replace('.', '/')),
267 builder.Constants.GetOrAddClass(name),
268 builder.Constants.GetOrAddUtf8(namePart),
269 (AccessFlag)innerType.ReflectiveModifiers);
270
271 any = true;
272 }
273 }
274
275 if (any)
276 builder.Attributes.Attribute(AttributeName.InnerClasses, blob);
277 }
278
284 void AddSignatureAttribute(ClassFileBuilder builder, RuntimeJavaType javaType)
285 {
286 var signature = javaType.GetGenericSignature();
287 if (signature != null)
288 builder.Attributes.Signature(signature);
289 }
290
296 void AddAssemblyAttribute(ClassFileBuilder builder, RuntimeJavaType javaType)
297 {
298
299 // consists of a U2 constant pointing to the full name of the assembly
300 var assemblyAttributeBlob = new BlobBuilder();
301 new ClassFormatWriter(assemblyAttributeBlob.ReserveBytes(ClassFormatWriter.U2).GetBytes()).WriteU2(builder.Constants.GetOrAddUtf8(GetAssemblyName(javaType)).Slot);
302 builder.Attributes.Encoder.Attribute(builder.Constants.GetOrAddUtf8("IKVM.NET.Assembly"), assemblyAttributeBlob);
303 }
304
313 void AddExceptionsAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaMethod method, AttributeTableBuilder attributes, MethodBase methodBase)
314 {
315 var throws = context.AttributeHelper.GetThrows(methodBase);
316 if (throws == null)
317 {
318 var throwsArray = method.GetDeclaredExceptions();
319 if (throwsArray != null && throwsArray.Length > 0)
320 {
321 attributes.Exceptions(e =>
322 {
323 foreach (string ex in throwsArray)
324 e.Class(builder.Constants.GetOrAddClass(ex.Replace('.', '/')));
325 });
326 }
327 }
328 else
329 {
330 if (throws.classes != null || throws.types != null)
331 {
332 attributes.Exceptions(e =>
333 {
334 if (throws.classes != null)
335 foreach (string ex in throws.classes)
336 e.Class(builder.Constants.GetOrAddClass(ex.Replace('.', '/')));
337
338 if (throws.types != null)
339 foreach (Type ex in throws.types)
340 e.Class(builder.Constants.GetOrAddClass(context.ClassLoaderFactory.GetJavaTypeFromType(ex).Name.Replace('.', '/')));
341 });
342 }
343 }
344 }
345
346
355 void AddMethodParameters(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaMethod method, AttributeTableBuilder attributes, bool includeParameterNames)
356 {
357 if (includeParameterNames)
358 {
359 var mp = type.GetMethodParameters(method);
360 if (mp == MethodParametersEntry.Malformed)
361 {
362 attributes.MethodParameters(e => { });
363 }
364 else if (mp != null)
365 {
366 attributes.MethodParameters(e =>
367 {
368 foreach (var i in mp)
369 e.MethodParameter(builder.Constants.GetOrAddUtf8(i.name), i.accessFlags);
370 });
371 }
372 }
373 }
374
383 void AddAnnotationDefaultAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaMethod method, AttributeTableBuilder attributes, MethodBase methodBase)
384 {
385 var attr = GetAnnotationDefault(methodBase);
386 if (attr != null)
387 attributes.AnnotationDefault(e => EncodeAnnotationDefault(builder, ref e, attr.ConstructorArguments[0]));
388 }
389
397 void AddSignatureAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaMethod method, AttributeTableBuilder methodAttributes)
398 {
399 var signature = type.GetGenericMethodSignature(method);
400 if (signature != null)
401 methodAttributes.Signature(signature);
402 }
403
412 void AddDeprecatedAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaMethod method, AttributeTableBuilder attributes, MethodBase methodBase)
413 {
414 // HACK the instancehelper methods are marked as Obsolete (to direct people toward the ikvm.extensions methods instead)
415 // but in the Java world most of them are not deprecated (and to keep the Japi results clean we need to reflect this)
416 // the Java deprecated methods actually have two Obsolete attributes
417 if (methodBase.IsDefined(context.Resolver.ResolveCoreType(typeof(ObsoleteAttribute).FullName).AsReflection(), false) && (!methodBase.Name.StartsWith("instancehelper_") || methodBase.DeclaringType.FullName != "java.lang.String" || GetObsoleteCount(methodBase) == 2))
418 attributes.Deprecated();
419 }
420
428 void AddRuntimeVisibleParameterAnnotationsAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaMethod method, AttributeTableBuilder attributes)
429 {
430 var blob = new BlobBuilder();
431 var encoder = new ParameterAnnotationTableEncoder(blob);
432 if (EncodeParameterAnnotations(builder, ref encoder, method.GetMethod()))
433 attributes.Attribute(AttributeName.RuntimeVisibleParameterAnnotations, blob);
434 }
435
443 void AddRuntimeVisibleTypeAnnotationsAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaMethod method, AttributeTableBuilder attributes)
444 {
445 var blob = new BlobBuilder();
446 var encoder = new TypeAnnotationTableEncoder(blob);
447 if (ImportTypeAnnotations(builder, ref encoder, type, type.GetMethodRawTypeAnnotations(method)))
448 attributes.Attribute(AttributeName.RuntimeVisibleTypeAnnotations, blob);
449 }
450
458 void AddRuntimeVisibleAnnotationsAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaMethod method, AttributeTableBuilder attributes)
459 {
460 var blob = new BlobBuilder();
461 var encoder = new AnnotationTableEncoder(blob);
462 if (EncodeAnnotations(builder, ref encoder, method.GetMethod()))
463 attributes.Attribute(AttributeName.RuntimeVisibleAnnotations, blob);
464 }
465
474 void AddConstantValueAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaField field, AttributeTableBuilder attributes)
475 {
476 if (field.GetField() != null && field.GetField().IsLiteral && (field.FieldTypeWrapper.IsPrimitive || field.FieldTypeWrapper == context.JavaBase.TypeOfJavaLangString))
477 {
478 var constant = field.GetField().GetRawConstantValue();
479 if (field.GetField().FieldType.IsEnum)
480 constant = EnumHelper.GetPrimitiveValue(context, EnumHelper.GetUnderlyingType(field.GetField().FieldType), constant);
481
482 if (constant != null)
483 {
484 switch (constant)
485 {
486 case int i:
487 attributes.ConstantValue(i);
488 break;
489 case short s:
490 attributes.ConstantValue(s);
491 break;
492 case char c:
493 attributes.ConstantValue(c);
494 break;
495 case byte b:
496 attributes.ConstantValue((int)(sbyte)b);
497 break;
498 case bool z:
499 attributes.ConstantValue(z);
500 break;
501 case float f:
502 attributes.ConstantValue(f);
503 break;
504 case long j:
505 attributes.ConstantValue(j);
506 break;
507 case double d:
508 attributes.ConstantValue(d);
509 break;
510 case string l:
511 attributes.ConstantValue(l);
512 break;
513 default:
514 throw new Exception();
515 }
516 }
517 }
518 }
519
527 void AddSignatureAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaField field, AttributeTableBuilder attributes)
528 {
529 var signature = type.GetGenericFieldSignature(field);
530 if (signature != null)
531 attributes.Signature(signature);
532 }
533
541 void AddDeprecatedAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaField field, AttributeTableBuilder attributes)
542 {
543 // .NET ObsoleteAttribute translates to Deprecated attribute
544 if (field.GetField() != null && field.GetField().IsDefined(context.Resolver.ResolveCoreType(typeof(ObsoleteAttribute).FullName).AsReflection(), false))
545 attributes.Deprecated();
546 }
547
555 void AddRuntimeVisibleTypeAnnotationsAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaField field, AttributeTableBuilder attributes)
556 {
557 var blob = new BlobBuilder();
558 var encoder = new TypeAnnotationTableEncoder(blob);
559 if (ImportTypeAnnotations(builder, ref encoder, type, type.GetFieldRawTypeAnnotations(field)))
560 attributes.Attribute(AttributeName.RuntimeVisibleTypeAnnotations, blob);
561 }
562
570 void AddRuntimeVisibleAnnotationsAttribute(ClassFileBuilder builder, RuntimeJavaType type, RuntimeJavaField field, AttributeTableBuilder attributes)
571 {
572 var blob = new BlobBuilder();
573 var encoder = new AnnotationTableEncoder(blob);
574 if (EncodeAnnotations(builder, ref encoder, field.GetField()))
575 attributes.Attribute(AttributeName.RuntimeVisibleAnnotations, blob);
576 }
577
584 bool EncodeAnnotations(ClassFileBuilder builder, ref AnnotationTableEncoder encoder, MemberInfo source)
585 {
586 var any = false;
587
588#if !FIRST_PASS && !EXPORTER
589 if (source != null)
590 {
591 foreach (var cad in CustomAttributeData.GetCustomAttributes(source))
592 {
593 var ann = GetAnnotation(cad);
594 if (ann != null)
595 {
596 EncodeAnnotation(builder, ref encoder, ann);
597 any = true;
598 }
599 }
600 }
601#endif
602
603 return any;
604 }
605
612 bool EncodeParameterAnnotations(ClassFileBuilder builder, ref ParameterAnnotationTableEncoder encoder, MethodBase source)
613 {
614 var any = false;
615
616#if !FIRST_PASS && !EXPORTER
617 if (source != null)
618 {
619 var parameters = source.GetParameters();
620 if (parameters.Length > 0)
621 {
622 for (int i = 0; i < parameters.Length; i++)
623 {
624 encoder.ParameterAnnotation(e2 =>
625 {
626 foreach (var cad in CustomAttributeData.GetCustomAttributes(parameters[i]))
627 {
628 var ann = GetAnnotation(cad);
629 if (ann != null)
630 {
631 EncodeAnnotation(builder, ref e2, ann);
632 any = true;
633 }
634 }
635 });
636 }
637 }
638 }
639#endif
640
641 return any;
642 }
643
652 bool ImportTypeAnnotations(ClassFileBuilder builder, ref TypeAnnotationTableEncoder encoder, RuntimeJavaType type, byte[] typeAnnotations)
653 {
654#if !FIRST_PASS && !EXPORTER
655 if (typeAnnotations != null)
656 {
657 var reader = new ClassFormatReader(typeAnnotations);
658 if (TypeAnnotationTable.TryRead(ref reader, out var table) == false)
659 throw new Exception();
660
661 table.CopyTo(new ConstantView(type.GetConstantPool()), builder.Constants, ref encoder);
662 return table.Count > 0;
663 }
664#endif
665
666 return false;
667 }
668
672 class ConstantView : IConstantView
673 {
674
675 readonly object[] view;
676
681 public ConstantView(object[] view)
682 {
683 this.view = view;
684 }
685
686 public Constant Get(ConstantHandle handle)
687 {
688 return handle.Kind switch
689 {
690 ConstantKind.Utf8 => Get((Utf8ConstantHandle)handle),
691 ConstantKind.Integer => Get((IntegerConstantHandle)handle),
692 ConstantKind.Float => Get((FloatConstantHandle)handle),
693 ConstantKind.Long => Get((LongConstantHandle)handle),
694 ConstantKind.Double => Get((DoubleConstantHandle)handle),
695 ConstantKind.Class => Get((ClassConstantHandle)handle),
696 ConstantKind.String => Get((StringConstantHandle)handle),
697 ConstantKind.Fieldref => Get((FieldrefConstantHandle)handle),
698 ConstantKind.Methodref => Get((MethodrefConstantHandle)handle),
699 ConstantKind.InterfaceMethodref => Get((InterfaceMethodrefConstantHandle)handle),
700 ConstantKind.NameAndType => Get((NameAndTypeConstantHandle)handle),
701 ConstantKind.MethodHandle => Get((MethodHandleConstantHandle)handle),
702 ConstantKind.MethodType => Get((MethodTypeConstantHandle)handle),
703 ConstantKind.Dynamic => Get((DynamicConstantHandle)handle),
704 ConstantKind.InvokeDynamic => Get((InvokeDynamicConstantHandle)handle),
705 ConstantKind.Module => Get((ModuleConstantHandle)handle),
706 ConstantKind.Package => Get((PackageConstantHandle)handle),
707 _ => Get((IntegerConstantHandle)handle),
708 };
709 }
710
711 public RefConstant Get(RefConstantHandle handle)
712 {
713 throw new NotImplementedException();
714 }
715
716 public Utf8Constant Get(Utf8ConstantHandle handle)
717 {
718 if (view[handle.Slot] is not string s)
719 throw new Exception();
720
721 return new Utf8Constant(s);
722 }
723
724 public IntegerConstant Get(IntegerConstantHandle handle)
725 {
726 if (view[handle.Slot] is not int i)
727 throw new Exception();
728
729 return new IntegerConstant(i);
730 }
731
732 public FloatConstant Get(FloatConstantHandle handle)
733 {
734 if (view[handle.Slot] is not float f)
735 throw new Exception();
736
737 return new FloatConstant(f);
738 }
739
740 public LongConstant Get(LongConstantHandle handle)
741 {
742 if (view[handle.Slot] is not long j)
743 throw new Exception();
744
745 return new LongConstant(j);
746 }
747
748 public DoubleConstant Get(DoubleConstantHandle handle)
749 {
750 if (view[handle.Slot] is not double d)
751 throw new Exception();
752
753 return new DoubleConstant(d);
754 }
755
756 public ClassConstant Get(ClassConstantHandle handle)
757 {
758 throw new NotImplementedException();
759 }
760
761 public StringConstant Get(StringConstantHandle handle)
762 {
763 if (view[handle.Slot] is not string s)
764 throw new Exception();
765
766 return new StringConstant(s);
767 }
768
769 public FieldrefConstant Get(FieldrefConstantHandle handle)
770 {
771 throw new NotImplementedException();
772 }
773
774 public MethodrefConstant Get(MethodrefConstantHandle handle)
775 {
776 throw new NotImplementedException();
777 }
778
779 public InterfaceMethodrefConstant Get(InterfaceMethodrefConstantHandle handle)
780 {
781 throw new NotImplementedException();
782 }
783
784 public NameAndTypeConstant Get(NameAndTypeConstantHandle handle)
785 {
786 throw new NotImplementedException();
787 }
788
789 public MethodHandleConstant Get(MethodHandleConstantHandle handle)
790 {
791 throw new NotImplementedException();
792 }
793
794 public MethodTypeConstant Get(MethodTypeConstantHandle handle)
795 {
796 throw new NotImplementedException();
797 }
798
799 public DynamicConstant Get(DynamicConstantHandle handle)
800 {
801 throw new NotImplementedException();
802 }
803
804 public InvokeDynamicConstant Get(InvokeDynamicConstantHandle handle)
805 {
806 throw new NotImplementedException();
807 }
808
809 public ModuleConstant Get(ModuleConstantHandle handle)
810 {
811 throw new NotImplementedException();
812 }
813
814 public PackageConstant Get(PackageConstantHandle handle)
815 {
816 throw new NotImplementedException();
817 }
818
819 }
820
821#if !FIRST_PASS && !EXPORTER
822
828 object[] GetAnnotation(CustomAttributeData cad)
829 {
830 // attribute is either a AnnotationAttributeBase or a DynamicAnnotationAttribute with a single object[] in our internal annotation format
831 if (cad.ConstructorArguments.Count == 1 && cad.ConstructorArguments[0].ArgumentType == typeof(object[]) && (cad.Constructor.DeclaringType.BaseType == typeof(ikvm.@internal.AnnotationAttributeBase) || cad.Constructor.DeclaringType == typeof(DynamicAnnotationAttribute)))
832 {
833 return UnpackArray((IList<CustomAttributeTypedArgument>)cad.ConstructorArguments[0].Value);
834 }
835
836 if (cad.Constructor.DeclaringType.BaseType == typeof(ikvm.@internal.AnnotationAttributeBase))
837 {
838 var annotationType = GetAnnotationInterface(cad);
839 if (annotationType != null)
840 {
841 // this is a custom attribute annotation applied in a non-Java module
842 var list = new List<object>();
844 list.Add("L" + annotationType.Replace('.', '/') + ";");
845
846 var parameters = cad.Constructor.GetParameters();
847 for (int i = 0; i < parameters.Length; i++)
848 {
849 list.Add(parameters[i].Name);
850 list.Add(GetAnnotationValue(cad.ConstructorArguments[i]));
851 }
852
853 foreach (var arg in cad.NamedArguments)
854 {
855 list.Add(arg.MemberInfo.Name);
856 list.Add(GetAnnotationValue(arg.TypedValue));
857 }
858
859 return list.ToArray();
860 }
861 }
862
863 return null;
864 }
865
866 string GetAnnotationInterface(CustomAttributeData cad)
867 {
868 var attr = cad.Constructor.DeclaringType.GetCustomAttributes(typeof(IKVM.Attributes.ImplementsAttribute), false);
869 if (attr.Length == 1)
870 {
871 var interfaces = ((IKVM.Attributes.ImplementsAttribute)attr[0]).Interfaces;
872 if (interfaces.Length == 1)
873 return interfaces[0];
874 }
875
876 return null;
877 }
878
885 object GetAnnotationValue(CustomAttributeTypedArgument arg)
886 {
887 // argument is directly an enum, so we encode it as a TAG_ENUM
888 if (arg.ArgumentType.IsEnum)
889 {
890 // if GetWrapperFromType returns null, we've got an ikvmc synthesized .NET enum nested inside a Java enum
891 var tw = context.ClassLoaderFactory.GetJavaTypeFromType(arg.ArgumentType) ?? context.ClassLoaderFactory.GetJavaTypeFromType(arg.ArgumentType.DeclaringType);
892 return new object[] { IKVM.Attributes.AnnotationDefaultAttribute.TAG_ENUM, EncodeTypeName(tw), Enum.GetName(arg.ArgumentType, arg.Value) };
893 }
894
895 // argument is directly a type, so we encode it as a TAG_CLASS
896 if (arg.Value is Type type)
897 {
898 return new object[] { IKVM.Attributes.AnnotationDefaultAttribute.TAG_CLASS, EncodeTypeName(context.ClassLoaderFactory.GetJavaTypeFromType(type)) };
899 }
900
901 // argument is directly an array, so we encode it a a TAG_ARRAY
902 if (arg.ArgumentType.IsArray)
903 {
904 var array = (IList<CustomAttributeTypedArgument>)arg.Value;
905 var arr = new object[array.Count + 1];
907 for (int i = 0; i < array.Count; i++)
908 arr[i + 1] = GetAnnotationValue(array[i]);
909
910 return arr;
911
912 }
913
914 return arg.Value;
915 }
916
923 void EncodeAnnotation(ClassFileBuilder builder, ref AnnotationTableEncoder encoder, object[] annotation)
924 {
925 encoder.Annotation(e =>
926 {
927 e.Annotation(builder.Constants.GetOrAddUtf8((string)annotation[1]), e2 =>
928 {
929 for (int i = 2; i < annotation.Length; i += 2)
930 e2.Element(builder.Constants.GetOrAddUtf8((string)annotation[i]), e3 => EncodeElementValue(builder, ref e3, annotation[i + 1]));
931 });
932 });
933 }
934
935 void EncodeElementValue(ClassFileBuilder builder, ref ElementValueEncoder encoder, object value)
936 {
937 if (value is object[] v)
938 {
940 {
941 encoder.Enum(builder.Constants.GetOrAddUtf8(DecodeTypeName((string)v[1])), builder.Constants.GetOrAddUtf8((string)v[2]));
942 return;
943 }
944
946 {
947 encoder.Array(e =>
948 {
949 for (int i = 1; i < v.Length; i++)
950 e.ElementValue(e2 => EncodeElementValue(builder, ref e2, v[i]));
951 });
952
953 return;
954 }
955
957 {
958 encoder.Class(builder.Constants.GetOrAddUtf8((string)v[1]));
959 return;
960 }
961
963 {
964 encoder.Annotation(e =>
965 {
966 e.Annotation(builder.Constants.GetOrAddUtf8(DecodeTypeName((string)v[1])), e2 =>
967 {
968 for (int i = 2; i < v.Length; i++)
969 e2.Element(builder.Constants.GetOrAddUtf8((string)v[i]), e3 => EncodeElementValue(builder, ref e3, v[i + 1]));
970 });
971 });
972
973 return;
974 }
975
976 throw new InvalidOperationException();
977 }
978
979 if (value is bool z)
980 {
981 encoder.Boolean(builder.Constants.GetOrAddInteger(z ? 1 : 0));
982 return;
983 }
984
985 if (value is byte b)
986 {
987 encoder.Byte(builder.Constants.GetOrAddInteger(unchecked((sbyte)b)));
988 return;
989 }
990
991 if (value is char c)
992 {
993 encoder.Char(builder.Constants.GetOrAddInteger(c));
994 return;
995 }
996
997 if (value is short s)
998 {
999 encoder.Short(builder.Constants.GetOrAddInteger(s));
1000 return;
1001 }
1002
1003 if (value is int i)
1004 {
1005 encoder.Integer(builder.Constants.GetOrAddInteger(i));
1006 return;
1007 }
1008
1009 if (value is long j)
1010 {
1011 encoder.Long(builder.Constants.GetOrAddLong(j));
1012 return;
1013 }
1014
1015 if (value is float f)
1016 {
1017 encoder.Float(builder.Constants.GetOrAddFloat(f));
1018 return;
1019 }
1020
1021 if (value is double d)
1022 {
1023 encoder.Double(builder.Constants.GetOrAddDouble(d));
1024 return;
1025 }
1026
1027 if (value is string S)
1028 {
1029 encoder.String(builder.Constants.GetOrAddUtf8(S));
1030 return;
1031 }
1032
1033 throw new InvalidOperationException();
1034 }
1035
1036 static string EncodeTypeName(RuntimeJavaType tw)
1037 {
1038 return tw.SigName.Replace('.', '/');
1039 }
1040
1041 static string DecodeTypeName(string typeName)
1042 {
1043#if !FIRST_PASS && !EXPORTER
1044 int index = typeName.IndexOf(',');
1045 if (index > 0)
1046 {
1047 // HACK if we have an assembly qualified type name we have to resolve it to a Java class name
1048 // (at the very least we should use the right class loader here)
1049 try
1050 {
1051 typeName = "L" + java.lang.Class.forName(typeName.Substring(1, typeName.Length - 2).Replace('/', '.')).getName().Replace('.', '/') + ";";
1052 }
1053 catch
1054 {
1055
1056 }
1057 }
1058#endif
1059
1060 return typeName;
1061 }
1062
1063#endif
1064
1065 object[] UnpackArray(IList<CustomAttributeTypedArgument> list)
1066 {
1067 var arr = new object[list.Count];
1068 for (int i = 0; i < arr.Length; i++)
1069 arr[i] = list[i].Value is IList<CustomAttributeTypedArgument> l ? UnpackArray(l) : list[i].Value;
1070
1071 return arr;
1072 }
1073
1074 int GetObsoleteCount(MethodBase mb)
1075 {
1076#if EXPORTER
1077 return mb.__GetCustomAttributes(context.Resolver.ResolveCoreType(typeof(ObsoleteAttribute).FullName).AsReflection(), false).Count;
1078#else
1079 return mb.GetCustomAttributes(typeof(ObsoleteAttribute), false).Length;
1080#endif
1081 }
1082
1083 CustomAttributeData GetAnnotationDefault(MethodBase mb)
1084 {
1085#if EXPORTER
1086 var attr = CustomAttributeData.__GetCustomAttributes(mb, context.Resolver.ResolveRuntimeType(typeof(Attributes.AnnotationDefaultAttribute).FullName).AsReflection(), false);
1087 return attr.Count == 1 ? attr[0] : null;
1088#else
1089 foreach (var cad in CustomAttributeData.GetCustomAttributes(mb))
1090 if (cad.Constructor.DeclaringType == typeof(Attributes.AnnotationDefaultAttribute))
1091 return cad;
1092
1093 return null;
1094#endif
1095 }
1096
1097 string GetAssemblyName(RuntimeJavaType tw)
1098 {
1099 var loader = tw.ClassLoader;
1100 var acl = loader as RuntimeAssemblyClassLoader;
1101 if (acl != null)
1102 return acl.GetAssembly(tw).FullName;
1103 else
1104 return ((RuntimeGenericClassLoader)loader).GetName();
1105 }
1106
1107 bool IsSerializable(RuntimeJavaType tw)
1108 {
1109 if (tw.Name == "java.io.Serializable")
1110 return true;
1111
1112 while (tw != null)
1113 {
1114 foreach (var iface in tw.Interfaces)
1115 if (IsSerializable(iface))
1116 return true;
1117
1118 tw = tw.BaseTypeWrapper;
1119 }
1120
1121 return false;
1122 }
1123
1131 bool EncodeAnnotationsForType(ClassFileBuilder builder, ref AnnotationTableEncoder encoder, RuntimeJavaType tw)
1132 {
1133 if (tw is RuntimeManagedJavaType.AttributeAnnotationJavaTypeBase attributeAnnotation)
1134 {
1135 encoder.Annotation(
1136 builder.Constants.GetOrAddUtf8("Ljava/lang/annotation/Retention;"), e => e
1137 .Enum(
1138 builder.Constants.GetOrAddUtf8("value"),
1139 builder.Constants.GetOrAddUtf8("Ljava/lang/annotation/RetentionPolicy;"),
1140 builder.Constants.GetOrAddUtf8("RUNTIME")));
1141
1142 var validOn = attributeAnnotation.AttributeTargets;
1143 var elementTypes = new List<string>();
1144
1145 if ((validOn & (AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate | AttributeTargets.Assembly)) != 0)
1146 elementTypes.Add("TYPE");
1147
1148 if ((validOn & AttributeTargets.Constructor) != 0)
1149 elementTypes.Add("CONSTRUCTOR");
1150
1151 if ((validOn & AttributeTargets.Field) != 0)
1152 elementTypes.Add("FIELD");
1153
1154 if ((validOn & (AttributeTargets.Method | AttributeTargets.ReturnValue)) != 0)
1155 elementTypes.Add("METHOD");
1156
1157 if ((validOn & AttributeTargets.Parameter) != 0)
1158 elementTypes.Add("PARAMETER");
1159
1160 encoder.Annotation(
1161 builder.Constants.GetOrAddUtf8("Ljava/lang/annotation/Target;"), e => e
1162 .Element(
1163 builder.Constants.GetOrAddUtf8("value"), e2 => e2
1164 .Array(e3 =>
1165 {
1166 foreach (var elementType in elementTypes)
1167 e3.Enum(builder.Constants.GetOrAddUtf8("Ljava/lang/annotation/ElementType;"), builder.Constants.GetOrAddUtf8(elementType));
1168 })));
1169
1170 if (IsRepeatableAnnotation(tw))
1171 {
1172 encoder.Annotation(
1173 builder.Constants.GetOrAddUtf8("Ljava/lang/annotation/Repeatable;"), e => e
1174 .Class(
1175 builder.Constants.GetOrAddUtf8("value"),
1176 builder.Constants.GetOrAddUtf8("L" + (tw.Name + RuntimeManagedJavaType.AttributeAnnotationMultipleSuffix).Replace('.', '/') + ";")));
1177 }
1178
1179 return true;
1180 }
1181
1182 return false;
1183 }
1184
1185 bool IsRepeatableAnnotation(RuntimeJavaType tw)
1186 {
1187 foreach (var nested in tw.InnerClasses)
1188 if (nested.Name == tw.Name + RuntimeManagedJavaType.AttributeAnnotationMultipleSuffix)
1189 return true;
1190
1191 return false;
1192 }
1193
1200 void EncodeAnnotationDefault(ClassFileBuilder builder, ref ElementValueEncoder encoder, CustomAttributeTypedArgument value)
1201 {
1202 EncodeElementValue(builder, ref encoder, value);
1203 }
1204
1212 void EncodeAnnotationDefault(ClassFileBuilder builder, ref ElementValueEncoder encoder, RuntimeJavaType type)
1213 {
1214 if (type == context.PrimitiveJavaTypeFactory.BOOLEAN)
1215 {
1216 encoder.Boolean(builder.Constants.GetOrAddInteger(0));
1217 }
1218 else if (type == context.PrimitiveJavaTypeFactory.BYTE)
1219 {
1220 encoder.Byte(builder.Constants.GetOrAddInteger(0));
1221 }
1222 else if (type == context.PrimitiveJavaTypeFactory.CHAR)
1223 {
1224 encoder.Char(builder.Constants.GetOrAddInteger(0));
1225 }
1226 else if (type == context.PrimitiveJavaTypeFactory.SHORT)
1227 {
1228 encoder.Short(builder.Constants.GetOrAddInteger(0));
1229 }
1230 else if (type == context.PrimitiveJavaTypeFactory.INT)
1231 {
1232 encoder.Integer(builder.Constants.GetOrAddInteger(0));
1233 }
1234 else if (type == context.PrimitiveJavaTypeFactory.FLOAT)
1235 {
1236 encoder.Float(builder.Constants.GetOrAddFloat(0));
1237 }
1238 else if (type == context.PrimitiveJavaTypeFactory.LONG)
1239 {
1240 encoder.Long(builder.Constants.GetOrAddLong(0));
1241 }
1242 else if (type == context.PrimitiveJavaTypeFactory.DOUBLE)
1243 {
1244 encoder.Double(builder.Constants.GetOrAddDouble(0));
1245 }
1246 else if (type == context.JavaBase.TypeOfJavaLangString)
1247 {
1248 encoder.String(builder.Constants.GetOrAddUtf8(""));
1249 }
1250 else if ((type.Modifiers & Modifiers.Enum) != 0)
1251 {
1252 encoder.Enum(builder.Constants.GetOrAddUtf8("L" + type.Name.Replace('.', '/') + ";"), builder.Constants.GetOrAddUtf8("__unspecified"));
1253 }
1254 else if (type == context.JavaBase.TypeOfJavaLangClass)
1255 {
1256 encoder.Class(builder.Constants.GetOrAddUtf8("Likvm/internal/__unspecified;"));
1257 }
1258 else if (type.IsArray)
1259 {
1260 encoder.Array(e => { });
1261 }
1262 else
1263 {
1264 throw new InvalidOperationException();
1265 }
1266 }
1267
1274 void EncodeElementValue(ClassFileBuilder builder, ref ElementValueTableEncoder encoder, CustomAttributeTypedArgument value)
1275 {
1276 encoder.ElementValue(e => EncodeElementValue(builder, ref e, value));
1277 }
1278
1285 void EncodeElementValue(ClassFileBuilder builder, ref ElementValueEncoder encoder, CustomAttributeTypedArgument value)
1286 {
1287 // typed argument of bool type holds BOOLEAN
1288 if (value.ArgumentType == context.Types.Boolean)
1289 {
1290 encoder.Boolean(builder.Constants.GetOrAddInteger((bool)value.Value ? 1 : 0));
1291 return;
1292 }
1293
1294 // typed argument of byte type holds BYTE
1295 if (value.ArgumentType == context.Types.Byte)
1296 {
1297 encoder.Byte(builder.Constants.GetOrAddInteger(unchecked((sbyte)(byte)value.Value)));
1298 return;
1299 }
1300
1301 // typed argument of char type holds CHAR
1302 if (value.ArgumentType == context.Types.Char)
1303 {
1304 encoder.Char(builder.Constants.GetOrAddInteger((char)value.Value));
1305 return;
1306 }
1307
1308 // typed argument of short type holds SHORT
1309 if (value.ArgumentType == context.Types.Int16)
1310 {
1311 encoder.Short(builder.Constants.GetOrAddInteger((short)value.Value));
1312 return;
1313 }
1314
1315 // typed argument of int type holds INTEGER
1316 if (value.ArgumentType == context.Types.Int32)
1317 {
1318 encoder.Integer(builder.Constants.GetOrAddInteger((int)value.Value));
1319 return;
1320 }
1321
1322 // typed argument of float type holds SINGLE
1323 if (value.ArgumentType == context.Types.Single)
1324 {
1325 encoder.Float(builder.Constants.GetOrAddFloat((float)value.Value));
1326 return;
1327 }
1328
1329 // typed argument of long type holds LONG
1330 if (value.ArgumentType == context.Types.Int64)
1331 {
1332 encoder.Long(builder.Constants.GetOrAddLong((long)value.Value));
1333 return;
1334 }
1335
1336 // typed argument double type holds DOUBLE
1337 if (value.ArgumentType == context.Types.Double)
1338 {
1339 encoder.Double(builder.Constants.GetOrAddDouble((double)value.Value));
1340 return;
1341 }
1342
1343 // typed argument of string type holds STRING
1344 if (value.ArgumentType == context.Types.String)
1345 {
1346 encoder.String(builder.Constants.GetOrAddUtf8((string)value.Value));
1347 return;
1348 }
1349
1350 // typed argument of array type holds ARRAY, CLASS, ENUM and ANNOTATION
1351 if (value.ArgumentType == context.Types.Object.MakeArrayType())
1352 {
1353 // first argument holds type in tag format
1354 var data = (IReadOnlyList<CustomAttributeTypedArgument>)value.Value;
1355 var type = (byte)data[0].Value;
1356
1357 // tag of ARRAY indicates ARRAY
1359 {
1360 encoder.Array(e =>
1361 {
1362 for (int i = 1; i < data.Count; i++)
1363 EncodeElementValue(builder, ref e, data[i]);
1364 });
1365
1366 return;
1367 }
1368
1369 // tag of CLASS indicates CLASS
1371 {
1372 encoder.Class(builder.Constants.GetOrAddUtf8((string)data[1].Value));
1373 return;
1374 }
1375
1376 // tag of ENUM indicates ENUM
1378 {
1379 encoder.Enum(builder.Constants.GetOrAddUtf8((string)data[1].Value), builder.Constants.GetOrAddUtf8((string)data[2].Value));
1380 return;
1381 }
1382
1383 // tag of ANNOTATION indicates ANNOTATION
1385 {
1386 encoder.Annotation(e =>
1387 {
1388 e.Annotation(builder.Constants.GetOrAddUtf8((string)data[1].Value), e2 =>
1389 {
1390 for (int i = 2; i < data.Count; i += 2)
1391 e2.Element(builder.Constants.GetOrAddUtf8((string)data[i].Value), e3 => EncodeElementValue(builder, ref e3, data[i + 1]));
1392 });
1393 });
1394
1395 return;
1396 }
1397
1398 Warning("Warning: incorrect annotation default element tag: " + type);
1399 return;
1400 }
1401
1402 Warning("Warning: incorrect annotation default element type: " + value.ArgumentType);
1403 return;
1404 }
1405
1410 void Warning(string message)
1411 {
1412#if EXPORTER
1413 Console.Error.WriteLine(message);
1414#endif
1415 }
1416
1417 }
1418
1419}
IKVM.Reflection.Type Type
IKVM.Reflection.MemberInfo MemberInfo
IKVM.Reflection.MethodBase MethodBase
global::java.lang.invoke.LambdaForm.Name Name
Maintains services relevant to an instane of the IKVM runtime.
StubGenerator(RuntimeContext context)
Initializes a new instance.