diff --git a/ikvmstub/ikvmstub.cs b/ikvmstub/ikvmstub.cs index 71446c9d..5f882cae 100644 --- a/ikvmstub/ikvmstub.cs +++ b/ikvmstub/ikvmstub.cs @@ -40,6 +40,7 @@ static class NetExp private static bool includeSerialVersionUID; private static bool includeNonPublicInterfaces; private static bool includeNonPublicMembers; + private static bool includeParameterNames; private static List namespaces = new List(); static int Main(string[] args) @@ -106,6 +107,10 @@ static class NetExp { forwarders = true; } + else if(s == "-parameters") + { + includeParameterNames = true; + } else { // unrecognized option, or multiple assemblies, print usage message and exit @@ -134,6 +139,7 @@ static class NetExp Console.Error.WriteLine(" -lib: Additional directories to search for references"); Console.Error.WriteLine(" -namespace: Only include types from specified namespace"); Console.Error.WriteLine(" -forwarders Export forwarded types too"); + Console.Error.WriteLine(" -parameters Emit Java 8 classes with parameter names"); return 1; } if(File.Exists(assemblyNameOrPath) && nostdlib) @@ -323,7 +329,7 @@ static class NetExp { zipCount++; MemoryStream mem = new MemoryStream(); - IKVM.StubGen.StubGenerator.WriteClass(mem, tw, includeNonPublicInterfaces, includeNonPublicMembers, includeSerialVersionUID); + IKVM.StubGen.StubGenerator.WriteClass(mem, tw, includeNonPublicInterfaces, includeNonPublicMembers, includeSerialVersionUID, includeParameterNames); ZipEntry entry = new ZipEntry(tw.Name.Replace('.', '/') + ".class"); entry.Size = mem.Position; zipFile.PutNextEntry(entry); @@ -387,6 +393,7 @@ static class NetExp try { ProcessClass(c); + WriteClass(c); } catch (Exception x) { @@ -400,7 +407,6 @@ static class NetExp throw; } } - WriteClass(c); } } } while(keepGoing); @@ -497,7 +503,7 @@ static class Intrinsics static class StaticCompiler { - internal static readonly Universe Universe = new Universe(); + internal static readonly Universe Universe = new Universe(UniverseOptions.EnableFunctionPointers); internal static readonly AssemblyResolver Resolver = new AssemblyResolver(); internal static Assembly runtimeAssembly; diff --git a/runtime/common.cs b/runtime/common.cs index 75f39be6..eff75684 100644 --- a/runtime/common.cs +++ b/runtime/common.cs @@ -49,7 +49,7 @@ namespace IKVM.NativeCode.gnu.java.net.protocol.ikvmres MemoryStream mem = new MemoryStream(); #if !FIRST_PASS bool includeNonPublicInterfaces = !"true".Equals(global::java.lang.Props.props.getProperty("ikvm.stubgen.skipNonPublicInterfaces"), StringComparison.OrdinalIgnoreCase); - IKVM.StubGen.StubGenerator.WriteClass(mem, TypeWrapper.FromClass(c), includeNonPublicInterfaces, false, false); + IKVM.StubGen.StubGenerator.WriteClass(mem, TypeWrapper.FromClass(c), includeNonPublicInterfaces, false, false, false); #endif return mem.ToArray(); } @@ -117,11 +117,7 @@ namespace IKVM.NativeCode.java.lang public static string getBootClassPath() { -#if FIRST_PASS - return null; -#else return VirtualFileSystem.GetAssemblyClassesPath(JVM.CoreAssembly); -#endif } } } @@ -146,60 +142,15 @@ namespace IKVM.NativeCode.ikvm.@internal } } - namespace stubgen + static class AnnotationAttributeBase { - static class StubGenerator + public static object newAnnotationInvocationHandler(jlClass type, object memberValues) { - public static int getRealModifiers(jlClass c) - { - return (int)TypeWrapper.FromClass(c).Modifiers; - } - - public static string getAssemblyName(jlClass c) - { - TypeWrapper wrapper = TypeWrapper.FromClass(c); - ClassLoaderWrapper loader = wrapper.GetClassLoader(); - IKVM.Internal.AssemblyClassLoader acl = loader as IKVM.Internal.AssemblyClassLoader; - if(acl != null) - { - return acl.GetAssembly(wrapper).FullName; - } - else - { - return ((GenericClassLoaderWrapper)loader).GetName(); - } - } - - public static object getFieldConstantValue(object field) - { - return FieldWrapper.FromField(field).GetConstant(); - } - - public static bool isFieldDeprecated(object field) - { - FieldWrapper fieldWrapper = FieldWrapper.FromField(field); - FieldInfo fi = fieldWrapper.GetField(); - if(fi != null) - { - return fi.IsDefined(typeof(ObsoleteAttribute), false); - } - return false; - } - - public static bool isMethodDeprecated(object method) - { - MethodWrapper mw = MethodWrapper.FromMethodOrConstructor(method); - MethodBase mb = mw.GetMethod(); - return mb != null && mb.IsDefined(typeof(ObsoleteAttribute), false); - } - - public static bool isClassDeprecated(jlClass clazz) - { - Type type = TypeWrapper.FromClass(clazz).TypeAsTBD; - // we need to check type for null, because ReflectionOnly - // generated delegate inner interfaces don't really exist - return type != null && type.IsDefined(typeof(ObsoleteAttribute), false); - } +#if FIRST_PASS + return null; +#else + return new global::sun.reflect.annotation.AnnotationInvocationHandler(type, (global::java.util.Map)memberValues); +#endif } } } @@ -228,7 +179,9 @@ namespace IKVM.NativeCode.ikvm.runtime TypeWrapper tw = wrapper.LoadClass(name); if (tw == null) { - throw new ClassNotFoundException(name); + Tracer.Info(Tracer.ClassLoading, "Failed to load class \"{0}\" from {1}", name, _this); + global::java.lang.Throwable.suppressFillInStackTrace = true; + throw new global::java.lang.ClassNotFoundException(name); } Tracer.Info(Tracer.ClassLoading, "Loaded class \"{0}\" from {1}", name, _this); return tw.ClassObject; diff --git a/runtime/stubgen/ClassFileWriter.cs b/runtime/stubgen/ClassFileWriter.cs index 327591c9..ded62674 100644 --- a/runtime/stubgen/ClassFileWriter.cs +++ b/runtime/stubgen/ClassFileWriter.cs @@ -830,6 +830,31 @@ namespace IKVM.StubGen } } + sealed class MethodParametersAttribute : ClassFileAttribute + { + private readonly ClassFileWriter classFile; + private readonly ushort[] names; + + internal MethodParametersAttribute(ClassFileWriter classFile, ushort[] names) + : base(classFile.AddUtf8("MethodParameters")) + { + this.classFile = classFile; + this.names = names; + } + + public override void Write(BigEndianStream bes) + { + base.Write(bes); + bes.WriteUInt32((uint)(1 + names.Length * 4)); + bes.WriteByte((byte)names.Length); + foreach (ushort idx in names) + { + bes.WriteUInt16(idx); + bes.WriteUInt16(0); + } + } + } + interface IAttributeOwner { void AddAttribute(ClassFileAttribute attrib); diff --git a/runtime/stubgen/StubGenerator.cs b/runtime/stubgen/StubGenerator.cs index 206cd182..1aa6f937 100644 --- a/runtime/stubgen/StubGenerator.cs +++ b/runtime/stubgen/StubGenerator.cs @@ -37,7 +37,7 @@ namespace IKVM.StubGen { static class StubGenerator { - internal static void WriteClass(Stream stream, TypeWrapper tw, bool includeNonPublicInterfaces, bool includeNonPublicMembers, bool includeSerialVersionUID) + internal static void WriteClass(Stream stream, TypeWrapper tw, bool includeNonPublicInterfaces, bool includeNonPublicMembers, bool includeSerialVersionUID, bool includeParameterNames) { string name = tw.Name.Replace('.', '/'); string super = null; @@ -49,7 +49,7 @@ namespace IKVM.StubGen { super = tw.BaseTypeWrapper.Name.Replace('.', '/'); } - ClassFileWriter writer = new ClassFileWriter(tw.Modifiers, name, super, 0, 49); + ClassFileWriter writer = new ClassFileWriter(tw.Modifiers, name, super, 0, includeParameterNames ? (ushort)52 : (ushort)49); foreach (TypeWrapper iface in tw.Interfaces) { if (iface.IsPublic || includeNonPublicInterfaces) @@ -185,6 +185,22 @@ namespace IKVM.StubGen { m.AddAttribute(new AnnotationDefaultClassFileAttribute(writer, GetAnnotationDefault(writer, attr.ConstructorArguments[0]))); } + if (includeParameterNames) + { + ParameterInfo[] parameters = mb.GetParameters(); + if (parameters.Length != 0) + { + ushort[] names = new ushort[parameters.Length]; + for (int i = 0; i < names.Length; i++) + { + if (parameters[i].Name != null) + { + names[i] = writer.AddUtf8(parameters[i].Name); + } + } + m.AddAttribute(new MethodParametersAttribute(writer, names)); + } + } } string sig = tw.GetGenericMethodSignature(mw); if (sig != null) @@ -252,7 +268,7 @@ namespace IKVM.StubGen { attr = new RuntimeVisibleAnnotationsAttribute(writer); } - attr.Add(UnpackArray((IList)cad.ConstructorArguments[0].Value)); + attr.Add(ann); } } if (attr != null) @@ -290,7 +306,7 @@ namespace IKVM.StubGen } param = new RuntimeVisibleAnnotationsAttribute(writer); } - param.Add(UnpackArray((IList)cad.ConstructorArguments[0].Value)); + param.Add(ann); } } if (attr != null) @@ -310,13 +326,84 @@ namespace IKVM.StubGen private static object[] GetAnnotation(CustomAttributeData cad) { if (cad.ConstructorArguments.Count == 1 && cad.ConstructorArguments[0].ArgumentType == typeof(object[]) && - (cad.Constructor.DeclaringType.IsSubclassOf(JVM.Import(typeof(ikvm.@internal.AnnotationAttributeBase))) - || cad.Constructor.DeclaringType == JVM.Import(typeof(DynamicAnnotationAttribute)))) + (cad.Constructor.DeclaringType.BaseType == typeof(ikvm.@internal.AnnotationAttributeBase) + || cad.Constructor.DeclaringType == typeof(DynamicAnnotationAttribute))) { return UnpackArray((IList)cad.ConstructorArguments[0].Value); } + else if (cad.Constructor.DeclaringType.BaseType == typeof(ikvm.@internal.AnnotationAttributeBase)) + { + string annotationType = GetAnnotationInterface(cad); + if (annotationType != null) + { + // this is a custom attribute annotation applied in a non-Java module + List list = new List(); + list.Add(AnnotationDefaultAttribute.TAG_ANNOTATION); + list.Add("L" + annotationType.Replace('.', '/') + ";"); + ParameterInfo[] parameters = cad.Constructor.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + list.Add(parameters[i].Name); + list.Add(EncodeAnnotationValue(cad.ConstructorArguments[i])); + } + foreach (CustomAttributeNamedArgument arg in cad.NamedArguments) + { + list.Add(arg.MemberInfo.Name); + list.Add(EncodeAnnotationValue(arg.TypedValue)); + } + return list.ToArray(); + } + } return null; } + + private static string GetAnnotationInterface(CustomAttributeData cad) + { + object[] attr = cad.Constructor.DeclaringType.GetCustomAttributes(typeof(IKVM.Attributes.ImplementsAttribute), false); + if (attr.Length == 1) + { + string[] interfaces = ((IKVM.Attributes.ImplementsAttribute)attr[0]).Interfaces; + if (interfaces.Length == 1) + { + return interfaces[0]; + } + } + return null; + } + + private static object EncodeAnnotationValue(CustomAttributeTypedArgument arg) + { + if (arg.ArgumentType.IsEnum) + { + // if GetWrapperFromType returns null, we've got an ikvmc synthesized .NET enum nested inside a Java enum + TypeWrapper tw = ClassLoaderWrapper.GetWrapperFromType(arg.ArgumentType) ?? ClassLoaderWrapper.GetWrapperFromType(arg.ArgumentType.DeclaringType); + return new object[] { AnnotationDefaultAttribute.TAG_ENUM, EncodeTypeName(tw), Enum.GetName(arg.ArgumentType, arg.Value) }; + } + else if (arg.Value is Type) + { + return new object[] { AnnotationDefaultAttribute.TAG_CLASS, EncodeTypeName(ClassLoaderWrapper.GetWrapperFromType((Type)arg.Value)) }; + } + else if (arg.ArgumentType.IsArray) + { + IList array = (IList)arg.Value; + object[] arr = new object[array.Count + 1]; + arr[0] = AnnotationDefaultAttribute.TAG_ARRAY; + for (int i = 0; i < array.Count; i++) + { + arr[i + 1] = EncodeAnnotationValue(array[i]); + } + return arr; + } + else + { + return arg.Value; + } + } + + private static string EncodeTypeName(TypeWrapper tw) + { + return tw.SigName.Replace('.', '/'); + } #endif private static object[] UnpackArray(IList list) @@ -438,10 +525,31 @@ namespace IKVM.StubGen "value", targets.ToArray() }); + if (Experimental.JDK_8 && IsRepeatableAnnotation(tw)) + { + annot.Add(new object[] { + AnnotationDefaultAttribute.TAG_ANNOTATION, + "Ljava/lang/annotation/Repeatable;", + "value", + new object[] { AnnotationDefaultAttribute.TAG_CLASS, "L" + (tw.Name + DotNetTypeWrapper.AttributeAnnotationMultipleSuffix).Replace('.', '/') + ";" } + }); + } writer.AddAttribute(annot); } } + private static bool IsRepeatableAnnotation(TypeWrapper tw) + { + foreach (TypeWrapper nested in tw.InnerClasses) + { + if (nested.Name == tw.Name + DotNetTypeWrapper.AttributeAnnotationMultipleSuffix) + { + return true; + } + } + return false; + } + private static byte[] GetAnnotationDefault(ClassFileWriter classFile, TypeWrapper type) { MemoryStream mem = new MemoryStream(); diff --git a/runtime/vfs.cs b/runtime/vfs.cs index 8db1165e..3ff13c3e 100644 --- a/runtime/vfs.cs +++ b/runtime/vfs.cs @@ -39,6 +39,25 @@ namespace IKVM.Internal || String.CompareOrdinal(path, 0, RootPath, 0, RootPath.Length) == 0; } + internal static string GetAssemblyClassesPath(Assembly asm) + { +#if FIRST_PASS + return null; +#else + // we can't use java.io.File.separatorChar here, because we're invoked by the system property setup code + return RootPath + "assembly" + System.IO.Path.DirectorySeparatorChar + VfsAssembliesDirectory.GetName(asm) + System.IO.Path.DirectorySeparatorChar + "classes" + System.IO.Path.DirectorySeparatorChar; +#endif + } + + internal static string GetAssemblyResourcesPath(Assembly asm) + { +#if FIRST_PASS + return null; +#else + return RootPath + "assembly" + System.IO.Path.DirectorySeparatorChar + VfsAssembliesDirectory.GetName(asm) + System.IO.Path.DirectorySeparatorChar + "resources" + System.IO.Path.DirectorySeparatorChar; +#endif + } + #if !FIRST_PASS private static VfsDirectory root; @@ -110,25 +129,6 @@ namespace IKVM.Internal } } - internal static string GetAssemblyClassesPath(Assembly asm) - { -#if FIRST_PASS - return null; -#else - // we can't use java.io.File.separatorChar here, because we're invoked by the system property setup code - return RootPath + "assembly" + System.IO.Path.DirectorySeparatorChar + VfsAssembliesDirectory.GetName(asm) + System.IO.Path.DirectorySeparatorChar + "classes" + System.IO.Path.DirectorySeparatorChar; -#endif - } - - internal static string GetAssemblyResourcesPath(Assembly asm) - { -#if FIRST_PASS - return null; -#else - return RootPath + "assembly" + System.IO.Path.DirectorySeparatorChar + VfsAssembliesDirectory.GetName(asm) + System.IO.Path.DirectorySeparatorChar + "resources" + System.IO.Path.DirectorySeparatorChar; -#endif - } - private sealed class VfsAssembliesDirectory : VfsDirectory { internal override VfsEntry GetEntry(string name) @@ -577,7 +577,7 @@ namespace IKVM.Internal { System.IO.MemoryStream mem = new System.IO.MemoryStream(); bool includeNonPublicInterfaces = !"true".Equals(java.lang.Props.props.getProperty("ikvm.stubgen.skipNonPublicInterfaces"), StringComparison.OrdinalIgnoreCase); - IKVM.StubGen.StubGenerator.WriteClass(mem, tw, includeNonPublicInterfaces, false, false); + IKVM.StubGen.StubGenerator.WriteClass(mem, tw, includeNonPublicInterfaces, false, false, false); buf = mem.ToArray(); } #endif @@ -985,7 +985,7 @@ namespace IKVM.Internal #if FIRST_PASS return false; #else - return access == IKVM.NativeCode.java.io.Win32FileSystem.ACCESS_READ && GetVfsEntry(path) != null; + return access == Java_java_io_Win32FileSystem.ACCESS_READ && GetVfsEntry(path) != null; #endif }