IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
RuntimeManagedJavaType.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2015 Jeroen Frijters
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
19
20 Jeroen Frijters
21 jeroen@frijters.net
22
23*/
24using System;
25using System.Collections.Generic;
26using System.Diagnostics;
27
28using IKVM.Attributes;
29
30using System.Text;
31
32
33#if IMPORTER || EXPORTER
34using IKVM.Reflection;
36
37using Type = IKVM.Reflection.Type;
38#else
39using System.Reflection;
40using System.Reflection.Emit;
41#endif
42
43namespace IKVM.Runtime
44{
45
49 sealed partial class RuntimeManagedJavaType : RuntimeJavaType
50 {
51
52 const string NamePrefix = "cli.";
53
54 internal const string DelegateInterfaceSuffix = "$Method";
55 internal const string AttributeAnnotationSuffix = "$Annotation";
56 internal const string AttributeAnnotationReturnValueSuffix = "$__ReturnValue";
57 internal const string AttributeAnnotationMultipleSuffix = "$__Multiple";
58 internal const string EnumEnumSuffix = "$__Enum";
59 internal const string GenericEnumEnumTypeName = "ikvm.internal.EnumEnum`1";
60 internal const string GenericDelegateInterfaceTypeName = "ikvm.internal.DelegateInterface`1";
61 internal const string GenericAttributeAnnotationTypeName = "ikvm.internal.AttributeAnnotation`1";
62 internal const string GenericAttributeAnnotationReturnValueTypeName = "ikvm.internal.AttributeAnnotationReturnValue`1";
63 internal const string GenericAttributeAnnotationMultipleTypeName = "ikvm.internal.AttributeAnnotationMultiple`1";
64
65 readonly Type type;
66 RuntimeJavaType baseTypeWrapper;
67 RuntimeJavaType[] innerClasses;
68 RuntimeJavaType outerClass;
69 RuntimeJavaType[] interfaces;
70
71 static Modifiers GetModifiers(Type type)
72 {
73 Modifiers modifiers = 0;
74 if (type.IsPublic)
75 {
76 modifiers |= Modifiers.Public;
77 }
78 else if (type.IsNestedPublic)
79 {
80 modifiers |= Modifiers.Static;
81 if (type.IsVisible)
82 {
83 modifiers |= Modifiers.Public;
84 }
85 }
86 else if (type.IsNestedPrivate)
87 {
88 modifiers |= Modifiers.Private | Modifiers.Static;
89 }
90 else if (type.IsNestedFamily || type.IsNestedFamORAssem)
91 {
92 modifiers |= Modifiers.Protected | Modifiers.Static;
93 }
94 else if (type.IsNestedAssembly || type.IsNestedFamANDAssem)
95 {
96 modifiers |= Modifiers.Static;
97 }
98
99 if (type.IsSealed)
100 {
101 modifiers |= Modifiers.Final;
102 }
103 else if (type.IsAbstract) // we can't be abstract if we're final
104 {
105 modifiers |= Modifiers.Abstract;
106 }
107 if (type.IsInterface)
108 {
109 modifiers |= Modifiers.Interface;
110 }
111
112 return modifiers;
113 }
114
115 // NOTE when this is called on a remapped type, the "warped" underlying type name is returned.
116 // E.g. GetName(typeof(object)) returns "cli.System.Object".
117 internal static string GetName(RuntimeContext context, Type type)
118 {
119 Debug.Assert(!type.Name.EndsWith("[]") && !context.AttributeHelper.IsJavaModule(type.Module));
120
121 var name = type.FullName;
122 if (name == null)
123 {
124 // generic type parameters don't have a full name
125 return null;
126 }
127
128 if (type.IsGenericType && !type.ContainsGenericParameters)
129 {
130 var sb = new ValueStringBuilder();
131 sb.Append(MangleTypeName(type.GetGenericTypeDefinition().FullName));
132 sb.Append("_$$$_");
133 var sep = "";
134 foreach (var t1 in type.GetGenericArguments())
135 {
136 var t = t1;
137 sb.Append(sep);
138
139 // NOTE we can't use ClassLoaderWrapper.GetWrapperFromType() here to get t's name,
140 // because we might be resolving a generic type that refers to a type that is in
141 // the process of being constructed.
142 //
143 // For example:
144 // class Base<T> { }
145 // class Derived : Base<Derived> { }
146 //
147 while (ReflectUtil.IsVector(t))
148 {
149 t = t.GetElementType();
150 sb.Append('A');
151 }
152
153 if (RuntimePrimitiveJavaType.IsPrimitiveType(context, t))
154 {
155 sb.Append(context.ClassLoaderFactory.GetJavaTypeFromType(t).SigName);
156 }
157 else
158 {
159 string s;
160 if (context.ClassLoaderFactory.IsRemappedType(t) || context.AttributeHelper.IsJavaModule(t.Module))
161 s = context.ClassLoaderFactory.GetJavaTypeFromType(t).Name;
162 else
163 s = RuntimeManagedJavaType.GetName(context, t);
164
165 // only do the mangling for non-generic types (because we don't want to convert
166 // the double underscores in two adjacent _$$$_ or _$$$$_ markers)
167 if (s.IndexOf("_$$$_") == -1)
168 {
169 s = s.Replace("__", "$$005F$$005F");
170 s = s.Replace(".", "__");
171 }
172
173 sb.Append('L');
174 sb.Append(s);
175 }
176
177 sep = "_$$_";
178 }
179
180 sb.Append("_$$$$_");
181 return sb.ToString();
182 }
183
184 if (context.AttributeHelper.IsNoPackagePrefix(type) && name.IndexOf('$') == -1)
185 return name.Replace('+', '$');
186
187 return MangleTypeName(name);
188 }
189
190 static string MangleTypeName(string name)
191 {
192 var sb = new ValueStringBuilder(NamePrefix.Length + name.Length);
193 sb.Append(NamePrefix);
194
195 var escape = false;
196 var nested = false;
197 for (int i = 0; i < name.Length; i++)
198 {
199 var c = name[i];
200 if (c == '+' && !escape && (sb.Length == 0 || sb[sb.Length - 1] != '$'))
201 {
202 nested = true;
203 sb.Append('$');
204 }
205 else if ("_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(c) != -1 || (c == '.' && !escape && !nested))
206 {
207 sb.Append(c);
208 }
209 else
210 {
211 sb.Append("$$");
212 sb.Append(string.Format("{0:X4}", (int)c));
213 }
214 if (c == '\\')
215 {
216 escape = !escape;
217 }
218 else
219 {
220 escape = false;
221 }
222 }
223
224 return sb.ToString();
225 }
226
227 // NOTE if the name is not a valid mangled type name, no demangling is done and the
228 // original string is returned
229 // NOTE we don't enforce canonical form, this is not required, because we cannot
230 // guarantee it for unprefixed names anyway, so the caller is responsible for
231 // ensuring that the original name was in fact the canonical name.
232 internal static string DemangleTypeName(string name)
233 {
234 if (name.StartsWith(NamePrefix, StringComparison.Ordinal) == false)
235 return name.Replace('$', '+');
236
237 var sb = new ValueStringBuilder(name.Length - NamePrefix.Length);
238 for (int i = NamePrefix.Length; i < name.Length; i++)
239 {
240 var c = name[i];
241 if (c == '$')
242 {
243 if (i + 1 < name.Length && name[i + 1] != '$')
244 {
245 sb.Append('+');
246 }
247 else
248 {
249 i++;
250 if (i + 5 > name.Length)
251 return name;
252
253 int digit0 = "0123456789ABCDEF".IndexOf(name[++i]);
254 int digit1 = "0123456789ABCDEF".IndexOf(name[++i]);
255 int digit2 = "0123456789ABCDEF".IndexOf(name[++i]);
256 int digit3 = "0123456789ABCDEF".IndexOf(name[++i]);
257 if (digit0 == -1 || digit1 == -1 || digit2 == -1 || digit3 == -1)
258 return name;
259
260 sb.Append((char)((digit0 << 12) + (digit1 << 8) + (digit2 << 4) + digit3));
261 }
262 }
263 else
264 {
265 sb.Append(c);
266 }
267 }
268
269 return sb.ToString();
270 }
271
272 // TODO from a perf pov it may be better to allow creation of TypeWrappers,
273 // but to simply make sure they don't have ClassObject
274 internal static bool IsAllowedOutside(Type type)
275 {
276 // SECURITY we never expose types from IKVM.Runtime, because doing so would lead to a security hole,
277 // since the reflection implementation lives inside this assembly, all internal members would
278 // be accessible through Java reflection.
279#if !FIRST_PASS && !IMPORTER && !EXPORTER
280 if (type.Assembly == typeof(RuntimeManagedJavaType).Assembly)
281 return false;
282#endif
283
284 return true;
285 }
286
287 internal static RuntimeJavaType Create(RuntimeContext context, Type type, string name)
288 {
289 if (type.ContainsGenericParameters)
290 {
291 return new OpenGenericJavaType(context, type, name);
292 }
293 else
294 {
295 return new RuntimeManagedJavaType(context, type, name);
296 }
297 }
298
305 RuntimeManagedJavaType(RuntimeContext context, Type type, string name) :
306 base(context, TypeFlags.None, GetModifiers(type), name)
307 {
308 Debug.Assert(!type.IsByRef, type.FullName);
309 Debug.Assert(!type.IsPointer, type.FullName);
310 Debug.Assert(!type.Name.EndsWith("[]"), type.FullName);
311 Debug.Assert(type is not TypeBuilder, type.FullName);
312 Debug.Assert(!Context.AttributeHelper.IsJavaModule(type.Module));
313
314 this.type = type;
315 }
316
317 internal override RuntimeJavaType BaseTypeWrapper => baseTypeWrapper ??= Context.ManagedJavaTypeFactory.GetBaseJavaType(type);
318
319 internal override RuntimeClassLoader ClassLoader => type.IsGenericType ? Context.ClassLoaderFactory.GetGenericClassLoader(this) : Context.AssemblyClassLoaderFactory.FromAssembly(type.Assembly);
320
321 internal static string GetDelegateInvokeStubName(Type delegateType)
322 {
323 var delegateInvoke = delegateType.GetMethod("Invoke");
324 var parameters = delegateInvoke.GetParameters();
325
326 string name = null;
327 for (int i = 0; i < parameters.Length; i++)
328 if (parameters[i].ParameterType.IsByRef)
329 name = (name ?? "<Invoke>") + "_" + i;
330
331 return name ?? "Invoke";
332 }
333
334 protected override void LazyPublishMembers()
335 {
336 // special support for enums
337 if (type.IsEnum)
338 {
339 Type underlyingType = EnumHelper.GetUnderlyingType(type);
340 Type javaUnderlyingType;
341 if (underlyingType == Context.Types.SByte)
342 {
343 javaUnderlyingType = Context.Types.Byte;
344 }
345 else if (underlyingType == Context.Types.UInt16)
346 {
347 javaUnderlyingType = Context.Types.Int16;
348 }
349 else if (underlyingType == Context.Types.UInt32)
350 {
351 javaUnderlyingType = Context.Types.Int32;
352 }
353 else if (underlyingType == Context.Types.UInt64)
354 {
355 javaUnderlyingType = Context.Types.Int64;
356 }
357 else
358 {
359 javaUnderlyingType = underlyingType;
360 }
361
362 var fieldType = Context.ClassLoaderFactory.GetJavaTypeFromType(javaUnderlyingType);
363 var fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
364 var fieldsList = new List<RuntimeJavaField>();
365 for (int i = 0; i < fields.Length; i++)
366 {
367 if (fields[i].FieldType == type)
368 {
369 var name = fields[i].Name;
370 if (name == "Value")
371 name = "_Value";
372 else if (name.StartsWith("_") && name.EndsWith("Value"))
373 name = "_" + name;
374
375 var val = EnumHelper.GetPrimitiveValue(Context, underlyingType, fields[i].GetRawConstantValue());
376 fieldsList.Add(new RuntimeConstantJavaField(this, fieldType, name, fieldType.SigName, Modifiers.Public | Modifiers.Static | Modifiers.Final, fields[i], val, MemberFlags.None));
377 }
378 }
379 fieldsList.Add(new EnumValueJavaField(this, fieldType));
380 SetFields(fieldsList.ToArray());
381 SetMethods(new RuntimeJavaMethod[] { new EnumWrapJavaMethod(this, fieldType) });
382 }
383 else
384 {
385 var fieldsList = new List<RuntimeJavaField>();
386 var fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
387 for (int i = 0; i < fields.Length; i++)
388 {
389 // TODO for remapped types, instance fields need to be converted to static getter/setter methods
390 if (fields[i].FieldType.IsPointer)
391 {
392 // skip, pointer fields are not supported
393 }
394 else
395 {
396 // TODO handle name/signature clash
397 fieldsList.Add(CreateFieldWrapperDotNet(Context.AttributeHelper.GetModifiers(fields[i], true).Modifiers, fields[i].Name, fields[i].FieldType, fields[i]));
398 }
399 }
400 SetFields(fieldsList.ToArray());
401
402 var methodsList = new Dictionary<string, RuntimeJavaMethod>();
403
404 // special case for delegate constructors!
405 if (IsDelegate(Context, type))
406 {
407 var iface = InnerClasses[0];
408 var mw = new DelegateJavaMethod(this, (DelegateInnerClassJavaType)iface);
409 methodsList.Add(mw.Name + mw.Signature, mw);
410 }
411
412 // add a protected default constructor to MulticastDelegate to make it easier to define a delegate in Java
413 if (type == Context.Types.MulticastDelegate)
414 methodsList.Add("<init>()V", new MulticastDelegateCtorJavaMethod(this));
415
416 var constructors = type.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
417 for (int i = 0; i < constructors.Length; i++)
418 {
419 if (MakeMethodDescriptor(constructors[i], out var name, out var sig, out var args, out var ret))
420 {
421 var mw = CreateMethodWrapper(name, sig, args, ret, constructors[i], false);
422 var key = mw.Name + mw.Signature;
423 if (methodsList.ContainsKey(key) == false)
424 methodsList.Add(key, mw);
425 }
426 }
427
428 if (type.IsValueType && !methodsList.ContainsKey("<init>()V"))
429 {
430 // Value types have an implicit default ctor
431 methodsList.Add("<init>()V", new ValueTypeDefaultCtorJavaMethod(this));
432 }
433
434 var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
435 for (int i = 0; i < methods.Length; i++)
436 {
437 if (methods[i].IsStatic && type.IsInterface)
438 {
439 // skip, Java cannot deal with static methods on interfaces
440 }
441 else
442 {
443 if (MakeMethodDescriptor(methods[i], out var name, out var sig, out var args, out var ret))
444 {
445 if (!methods[i].IsStatic && !methods[i].IsPrivate && BaseTypeWrapper != null)
446 {
447 var baseMethod = BaseTypeWrapper.GetMethod(name, sig, true);
448 if (baseMethod != null && baseMethod.IsFinal && !baseMethod.IsStatic && !baseMethod.IsPrivate)
449 continue;
450 }
451
452 var mw = CreateMethodWrapper(name, sig, args, ret, methods[i], false);
453 var key = mw.Name + mw.Signature;
454 methodsList.TryGetValue(key, out var existing);
455
456 if (existing == null || existing is ByRefJavaMethod)
457 methodsList[key] = mw;
458 }
459 else if (methods[i].IsAbstract)
460 {
461 SetHasUnsupportedAbstractMethods();
462 }
463 }
464 }
465
466 // make sure that all the interface methods that we implement are available as public methods,
467 // otherwise javac won't like the class.
468 if (!type.IsInterface)
469 {
470 var interfaces = type.GetInterfaces();
471 for (int i = 0; i < interfaces.Length; i++)
472 {
473 // we only handle public (or nested public) types, because we're potentially adding a
474 // method that should be callable by anyone through the interface
475 if (interfaces[i].IsVisible)
476 {
477 if (Context.ClassLoaderFactory.IsRemappedType(interfaces[i]))
478 {
479 var tw = Context.ClassLoaderFactory.GetJavaTypeFromType(interfaces[i]);
480 foreach (var mw in tw.GetMethods())
481 {
482 // HACK we need to link here, because during a core library build we might reference java.lang.AutoCloseable (via IDisposable) before it has been linked
483 mw.Link();
484 InterfaceMethodStubHelper(methodsList, mw.GetMethod(), mw.Name, mw.Signature, mw.GetParameters(), mw.ReturnType);
485 }
486 }
487
488 var map = type.GetInterfaceMap(interfaces[i]);
489 for (int j = 0; j < map.InterfaceMethods.Length; j++)
490 {
491 if (map.TargetMethods[j] == null || ((!map.TargetMethods[j].IsPublic || map.TargetMethods[j].Name != map.InterfaceMethods[j].Name) && map.TargetMethods[j].DeclaringType == type))
492 {
493 if (MakeMethodDescriptor(map.InterfaceMethods[j], out var name, out var sig, out var args, out var ret))
494 {
495 InterfaceMethodStubHelper(methodsList, map.InterfaceMethods[j], name, sig, args, ret);
496 }
497 }
498 }
499 }
500 }
501 }
502
503 // for non-final remapped types, we need to add all the virtual methods in our alter ego (which
504 // appears as our base class) and make them final (to prevent Java code from overriding these
505 // methods, which don't really exist).
506 if (Context.ClassLoaderFactory.IsRemappedType(type) && !type.IsSealed && !type.IsInterface)
507 {
508 var baseTypeWrapper = BaseTypeWrapper;
509
510 while (baseTypeWrapper != null)
511 {
512 foreach (var m in baseTypeWrapper.GetMethods())
513 {
514 if (!m.IsStatic && !m.IsFinal && (m.IsPublic || m.IsProtected) && m.Name != "<init>")
515 {
516 var key = m.Name + m.Signature;
517 if (!methodsList.ContainsKey(key))
518 {
519 if (m.IsProtected)
520 {
521 if (m.Name == "finalize" && m.Signature == "()V")
522 {
523 methodsList.Add(key, new FinalizeJavaMethod(this));
524 }
525 else if (m.Name == "clone" && m.Signature == "()Ljava.lang.Object;")
526 {
527 methodsList.Add(key, new CloneJavaMethod(this));
528 }
529 else
530 {
531 // there should be a special MethodWrapper for this method
532 throw new InvalidOperationException("Missing protected method support for " + baseTypeWrapper.Name + "::" + m.Name + m.Signature);
533 }
534 }
535 else
536 {
537 methodsList.Add(key, new BaseFinalJavaMethod(this, m));
538 }
539 }
540 }
541 }
542
543 baseTypeWrapper = baseTypeWrapper.BaseTypeWrapper;
544 }
545 }
546
547#if !IMPORTER && !EXPORTER && !FIRST_PASS
548
549 // support serializing .NET exceptions (by replacing them with a placeholder exception)
550 if (typeof(Exception).IsAssignableFrom(type) && !typeof(java.io.Serializable.__Interface).IsAssignableFrom(type) && !methodsList.ContainsKey("writeReplace()Ljava.lang.Object;"))
551 {
552 methodsList.Add("writeReplace()Ljava.lang.Object;", new ExceptionWriteReplaceJavaMethod(this));
553 }
554
555#endif
556
557 var methodArray = new RuntimeJavaMethod[methodsList.Count];
558 methodsList.Values.CopyTo(methodArray, 0);
559 SetMethods(methodArray);
560 }
561 }
562
563 void InterfaceMethodStubHelper(Dictionary<string, RuntimeJavaMethod> methodsList, MethodBase method, string name, string sig, RuntimeJavaType[] args, RuntimeJavaType ret)
564 {
565 var key = name + sig;
566 methodsList.TryGetValue(key, out var existing);
567 if (existing == null && BaseTypeWrapper != null)
568 {
569 var baseMethod = BaseTypeWrapper.GetMethod(name, sig, true);
570 if (baseMethod != null && !baseMethod.IsStatic && baseMethod.IsPublic)
571 return;
572 }
573
574 if (existing == null || existing is ByRefJavaMethod || existing.IsStatic || !existing.IsPublic)
575 {
576 // TODO if existing != null, we need to rename the existing method (but this is complicated because
577 // it also affects subclasses). This is especially required is the existing method is abstract,
578 // because otherwise we won't be able to create any subclasses in Java.
579 methodsList[key] = CreateMethodWrapper(name, sig, args, ret, method, true);
580 }
581 }
582
583 internal static bool IsUnsupportedAbstractMethod(MethodBase mb)
584 {
585 if (mb.IsAbstract)
586 {
587 var mi = (MethodInfo)mb;
588 if (mi.ReturnType.IsByRef || IsPointerType(mi.ReturnType) || mb.IsGenericMethodDefinition)
589 return true;
590
591 foreach (var p in mi.GetParameters())
592 if (p.ParameterType.IsByRef || IsPointerType(p.ParameterType))
593 return true;
594 }
595
596 return false;
597 }
598
599 static bool IsPointerType(Type type)
600 {
601 while (type.HasElementType)
602 {
603 if (type.IsPointer)
604 return true;
605
606 type = type.GetElementType();
607 }
608
609#if IMPORTER || EXPORTER
610 return type.IsFunctionPointer;
611#else
612#if NET8_0_OR_GREATER
613 return type.IsFunctionPointer;
614#else
615 return false;
616#endif
617#endif
618 }
619
620 bool MakeMethodDescriptor(MethodBase mb, out string name, out string sig, out RuntimeJavaType[] args, out RuntimeJavaType ret)
621 {
622 if (mb.IsGenericMethodDefinition)
623 {
624 name = null;
625 sig = null;
626 args = null;
627 ret = null;
628 return false;
629 }
630
631 var sb = new ValueStringBuilder();
632 sb.Append('(');
633 var parameters = mb.GetParameters();
634 args = new RuntimeJavaType[parameters.Length];
635 for (int i = 0; i < parameters.Length; i++)
636 {
637 var type = parameters[i].ParameterType;
638 if (IsPointerType(type))
639 {
640 name = null;
641 sig = null;
642 args = null;
643 ret = null;
644 return false;
645 }
646
647 if (type.IsByRef)
648 {
649 type = RuntimeArrayJavaType.MakeArrayType(type.GetElementType(), 1);
650 if (mb.IsAbstract)
651 {
652 // Since we cannot override methods with byref arguments, we don't report abstract
653 // methods with byref args.
654 name = null;
655 sig = null;
656 args = null;
657 ret = null;
658 return false;
659 }
660 }
661
662 var tw = Context.ClassLoaderFactory.GetJavaTypeFromType(type);
663 args[i] = tw;
664 sb.Append(tw.SigName);
665 }
666 sb.Append(')');
667 if (mb is ConstructorInfo)
668 {
669 ret = Context.PrimitiveJavaTypeFactory.VOID;
670 name = mb.IsStatic ? "<clinit>" : "<init>";
671 sb.Append(ret.SigName);
672 sig = sb.ToString();
673 return true;
674 }
675 else
676 {
677 var type = ((MethodInfo)mb).ReturnType;
678 if (IsPointerType(type) || type.IsByRef)
679 {
680 name = null;
681 sig = null;
682 ret = null;
683 return false;
684 }
685 ret = Context.ClassLoaderFactory.GetJavaTypeFromType(type);
686 sb.Append(ret.SigName);
687 name = mb.Name;
688 sig = sb.ToString();
689 return true;
690 }
691 }
692
693 internal override RuntimeJavaType[] Interfaces => interfaces ??= GetImplementedInterfacesAsTypeWrappers(Context, type);
694
695 static bool IsAttribute(RuntimeContext context, Type type)
696 {
697 if (!type.IsAbstract && type.IsSubclassOf(context.Types.Attribute) && type.IsVisible)
698 {
699 //
700 // Based on the number of constructors and their arguments, we distinguish several types
701 // of attributes:
702 // | def ctor | single 1-arg ctor
703 // -----------------------------------------------------------------
704 // complex only (i.e. Annotation{N}) | |
705 // all optional fields/properties | X |
706 // required "value" | | X
707 // optional "value" | X | X
708 // -----------------------------------------------------------------
709 //
710 // TODO currently we don't support "complex only" attributes.
711 //
712 AttributeAnnotationJavaType.GetConstructors(context, type, out var defCtor, out var singleOneArgCtor);
713 return defCtor != null || singleOneArgCtor != null;
714 }
715
716 return false;
717 }
718
719 static bool IsDelegate(RuntimeContext context, Type type)
720 {
721 // HACK non-public delegates do not get the special treatment (because they are likely to refer to
722 // non-public types in the arg list and they're not really useful anyway)
723 // NOTE we don't have to check in what assembly the type lives, because this is a DotNetTypeWrapper,
724 // we know that it is a different assembly.
725 if (!type.IsAbstract && type.IsSubclassOf(context.Types.MulticastDelegate) && type.IsVisible)
726 {
727 var invoke = type.GetMethod("Invoke");
728 if (invoke != null)
729 {
730 foreach (var p in invoke.GetParameters())
731 {
732 // we don't support delegates with pointer parameters
733 if (IsPointerType(p.ParameterType))
734 return false;
735 }
736
737 return !IsPointerType(invoke.ReturnType);
738 }
739 }
740
741 return false;
742 }
743
744 internal override RuntimeJavaType[] InnerClasses => innerClasses ??= GetInnerClasses();
745
746 RuntimeJavaType[] GetInnerClasses()
747 {
748 var nestedTypes = type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
749 var list = new List<RuntimeJavaType>(nestedTypes.Length);
750 for (int i = 0; i < nestedTypes.Length; i++)
751 if (!nestedTypes[i].IsGenericTypeDefinition)
752 list.Add(Context.ClassLoaderFactory.GetJavaTypeFromType(nestedTypes[i]));
753
754 if (IsDelegate(Context, type))
755 list.Add(ClassLoader.RegisterInitiatingLoader(new DelegateInnerClassJavaType(Context, Name + DelegateInterfaceSuffix, type)));
756
757 if (IsAttribute(Context, type))
758 list.Add(ClassLoader.RegisterInitiatingLoader(new AttributeAnnotationJavaType(Context, Name + AttributeAnnotationSuffix, type)));
759
760 if (type.IsEnum && type.IsVisible)
761 list.Add(ClassLoader.RegisterInitiatingLoader(new EnumEnumJavaType(Context, Name + EnumEnumSuffix, type)));
762
763 return list.ToArray();
764 }
765
766 internal override bool IsFakeTypeContainer => IsDelegate(Context, type) || IsAttribute(Context, type) || (type.IsEnum && type.IsVisible);
767
768 internal override RuntimeJavaType DeclaringTypeWrapper
769 {
770 get
771 {
772 if (outerClass == null)
773 {
774 var outer = type.DeclaringType;
775 if (outer != null && !type.IsGenericType)
776 outerClass = Context.ManagedJavaTypeFactory.GetJavaTypeFromManagedType(outer);
777 }
778
779 return outerClass;
780 }
781 }
782
783 internal override Modifiers ReflectiveModifiers => DeclaringTypeWrapper != null ? Modifiers | Modifiers.Static : Modifiers;
784
785 RuntimeJavaField CreateFieldWrapperDotNet(Modifiers modifiers, string name, Type fieldType, FieldInfo field)
786 {
787 var type = Context.ClassLoaderFactory.GetJavaTypeFromType(fieldType);
788 if (field.IsLiteral)
789 return new RuntimeConstantJavaField(this, type, name, type.SigName, modifiers, field, null, MemberFlags.None);
790 else
791 return RuntimeJavaField.Create(this, type, field, name, type.SigName, new ExModifiers(modifiers, false));
792 }
793
799 static bool IsRemappedImplDerived(RuntimeContext context, Type type)
800 {
801 for (; type != null; type = type.BaseType)
802 if (!context.ClassLoaderFactory.IsRemappedType(type) && context.ClassLoaderFactory.GetJavaTypeFromType(type).IsRemapped)
803 return true;
804
805 return false;
806 }
807
808 RuntimeJavaMethod CreateMethodWrapper(string name, string sig, RuntimeJavaType[] argTypeWrappers, RuntimeJavaType retTypeWrapper, MethodBase mb, bool privateInterfaceImplHack)
809 {
810 var exmods = Context.AttributeHelper.GetModifiers(mb, true);
811 var mods = exmods.Modifiers;
812
813 if (name == "Finalize" && sig == "()V" && !mb.IsStatic && IsRemappedImplDerived(Context, TypeAsBaseType))
814 {
815 // TODO if the .NET also has a "finalize" method, we need to hide that one (or rename it, or whatever)
816 var mw = new RuntimeSimpleCallJavaMethod(this, "finalize", "()V", (MethodInfo)mb, Context.PrimitiveJavaTypeFactory.VOID, Array.Empty<RuntimeJavaType>(), mods, MemberFlags.None, SimpleOpCode.Call, SimpleOpCode.Callvirt);
817 mw.SetDeclaredExceptions(["java.lang.Throwable"]);
818 return mw;
819 }
820
821 var parameters = mb.GetParameters();
822 var args = new Type[parameters.Length];
823 var hasByRefArgs = false;
824 bool[] byrefs = null;
825
826 for (int i = 0; i < parameters.Length; i++)
827 {
828 args[i] = parameters[i].ParameterType;
829 if (parameters[i].ParameterType.IsByRef)
830 {
831 byrefs ??= new bool[args.Length];
832 byrefs[i] = true;
833 hasByRefArgs = true;
834 }
835 }
836
837 if (privateInterfaceImplHack)
838 {
839 mods &= ~Modifiers.Abstract;
840 mods |= Modifiers.Final;
841 }
842
843 if (hasByRefArgs)
844 {
845 if (mb is not ConstructorInfo && !mb.IsStatic)
846 mods |= Modifiers.Final;
847
848 return new ByRefJavaMethod(args, byrefs, this, name, sig, mb, retTypeWrapper, argTypeWrappers, mods, false);
849 }
850 else
851 {
852 return new RuntimeTypicalJavaMethod(this, name, sig, mb, retTypeWrapper, argTypeWrappers, mods, MemberFlags.None);
853 }
854 }
855
856 internal override Type TypeAsTBD => type;
857
858 internal override bool IsRemapped => Context.ClassLoaderFactory.IsRemappedType(type);
859
860#if EMITTERS
861
862 internal override void EmitInstanceOf(CodeEmitter ilgen)
863 {
864 if (IsRemapped)
865 {
866 var shadow = Context.ClassLoaderFactory.GetJavaTypeFromType(type);
867 var method = shadow.TypeAsBaseType.GetMethod("__<instanceof>");
868 if (method != null)
869 {
870 ilgen.Emit(OpCodes.Call, method);
871 return;
872 }
873 }
874
875 ilgen.Emit_instanceof(type);
876 }
877
878 internal override void EmitCheckcast(CodeEmitter ilgen)
879 {
880 if (IsRemapped)
881 {
882 var shadow = Context.ClassLoaderFactory.GetJavaTypeFromType(type);
883 var method = shadow.TypeAsBaseType.GetMethod("__<checkcast>");
884 if (method != null)
885 {
886 ilgen.Emit(OpCodes.Call, method);
887 return;
888 }
889 }
890 ilgen.EmitCastclass(type);
891 }
892
893#endif
894
895 internal override MethodParametersEntry[] GetMethodParameters(RuntimeJavaMethod mw)
896 {
897 var mb = mw.GetMethod();
898 if (mb == null)
899 return null;
900
901 var parameters = mb.GetParameters();
902 if (parameters.Length == 0)
903 return null;
904
905 var mp = new MethodParametersEntry[parameters.Length];
906 var hasName = false;
907 for (int i = 0; i < mp.Length; i++)
908 {
909 var name = parameters[i].Name;
910 var empty = string.IsNullOrEmpty(name);
911 if (empty)
912 name = "arg" + i;
913 mp[i].name = name;
914 hasName |= !empty;
915 }
916
917 if (!hasName)
918 return null;
919
920 return mp;
921 }
922
923#if !IMPORTER && !EXPORTER
924
925 internal override object[] GetDeclaredAnnotations()
926 {
927 return type.GetCustomAttributes(false);
928 }
929
930 internal override object[] GetFieldAnnotations(RuntimeJavaField fw)
931 {
932 var fi = fw.GetField();
933 if (fi == null)
934 return null;
935
936 return fi.GetCustomAttributes(false);
937 }
938
939 internal override object[] GetMethodAnnotations(RuntimeJavaMethod mw)
940 {
941 var mb = mw.GetMethod();
942 if (mb == null)
943 return null;
944
945 return mb.GetCustomAttributes(false);
946 }
947
948 internal override object[][] GetParameterAnnotations(RuntimeJavaMethod mw)
949 {
950 var mb = mw.GetMethod();
951 if (mb == null)
952 return null;
953
954 var parameters = mb.GetParameters();
955 var attribs = new object[parameters.Length][];
956 for (int i = 0; i < parameters.Length; i++)
957 attribs[i] = parameters[i].GetCustomAttributes(false);
958
959 return attribs;
960 }
961#endif
962
963 internal override bool IsFastClassLiteralSafe => type != Context.Types.Void && !type.IsPrimitive && !IsRemapped;
964
965#if !IMPORTER && !EXPORTER
966
967 // this override is only relevant for the runtime, because it handles the scenario
968 // where classes are dynamically loaded by the assembly class loader
969 // (i.e. injected into the assembly)
970 internal override bool IsPackageAccessibleFrom(RuntimeJavaType wrapper)
971 {
972 if (wrapper == DeclaringTypeWrapper)
973 return true;
974
975 if (!base.IsPackageAccessibleFrom(wrapper))
976 return false;
977
978 // check accessibility for nested types
979 for (Type type = TypeAsTBD; type.IsNested; type = type.DeclaringType)
980 {
981 // we don't support family (protected) access
982 if (!type.IsNestedAssembly && !type.IsNestedFamORAssem && !type.IsNestedPublic)
983 {
984 return false;
985 }
986 }
987
988 return true;
989 }
990
991#endif
992
993 }
994
995}
IKVM.Reflection.Type Type
IKVM.Reflection.Assembly Assembly
IKVM.Reflection.ConstructorInfo ConstructorInfo
IKVM.Reflection.FieldInfo FieldInfo
IKVM.Reflection.MethodInfo MethodInfo
IKVM.Reflection.MethodBase MethodBase
global::java.lang.invoke.LambdaForm.Name Name
AttributeHelper AttributeHelper
Gets the AttributeHelper associated with this instance of the runtime.
Types Types
Gets the Types associated with this instance of the runtime.
RuntimeManagedJavaTypeFactory ManagedJavaTypeFactory
Gets the RuntimeManagedJavaTypeFactory associated with this instance of the runtime.
RuntimePrimitiveJavaTypeFactory PrimitiveJavaTypeFactory
Gets the RuntimePrimitiveJavaTypeFactory associated with this instance of the runtime.
RuntimeClassLoaderFactory ClassLoaderFactory
Gets the RuntimeClassLoaderFactory associated with this instance of the runtime.
RuntimeJavaType GetJavaTypeFromManagedType(Type type)
Gets the RuntimeJavaType associated with the specified managed type, or creates one on demand.
Type MulticastDelegate
Definition Types.cs:109
MemberFlags
Describes various options applied to a member.