зеркало из https://github.com/mono/ikvm-fork.git
1082 строки
32 KiB
C#
1082 строки
32 KiB
C#
/*
|
|
Copyright (C) 2002-2012 Jeroen Frijters
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Jeroen Frijters
|
|
jeroen@frijters.net
|
|
|
|
*/
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using ICSharpCode.SharpZipLib.Zip;
|
|
using IKVM.Attributes;
|
|
using IKVM.Internal;
|
|
using IKVM.Reflection;
|
|
using Type = IKVM.Reflection.Type;
|
|
|
|
static class NetExp
|
|
{
|
|
private static int zipCount;
|
|
private static ZipOutputStream zipFile;
|
|
private static Dictionary<string, string> done = new Dictionary<string, string>();
|
|
private static Dictionary<string, TypeWrapper> todo = new Dictionary<string, TypeWrapper>();
|
|
private static FileInfo file;
|
|
private static bool includeSerialVersionUID;
|
|
private static bool includeNonPublicInterfaces;
|
|
private static bool includeNonPublicMembers;
|
|
private static List<string> namespaces = new List<string>();
|
|
|
|
static int Main(string[] args)
|
|
{
|
|
IKVM.Internal.Tracer.EnableTraceConsoleListener();
|
|
IKVM.Internal.Tracer.EnableTraceForDebug();
|
|
string assemblyNameOrPath = null;
|
|
bool continueOnError = false;
|
|
bool autoLoadSharedClassLoaderAssemblies = false;
|
|
List<string> references = new List<string>();
|
|
List<string> libpaths = new List<string>();
|
|
bool nostdlib = false;
|
|
bool bootstrap = false;
|
|
string outputFile = null;
|
|
bool forwarders = false;
|
|
foreach(string s in args)
|
|
{
|
|
if(s.StartsWith("-") || assemblyNameOrPath != null)
|
|
{
|
|
if(s == "-serialver")
|
|
{
|
|
Console.Error.WriteLine("The -serialver option is deprecated and will be removed in the future. Use -japi instead.");
|
|
includeSerialVersionUID = true;
|
|
}
|
|
else if(s == "-japi")
|
|
{
|
|
includeSerialVersionUID = true;
|
|
includeNonPublicInterfaces = true;
|
|
includeNonPublicMembers = true;
|
|
}
|
|
else if(s == "-skiperror")
|
|
{
|
|
continueOnError = true;
|
|
}
|
|
else if(s == "-shared")
|
|
{
|
|
autoLoadSharedClassLoaderAssemblies = true;
|
|
}
|
|
else if(s.StartsWith("-r:") || s.StartsWith("-reference:"))
|
|
{
|
|
references.Add(s.Substring(s.IndexOf(':') + 1));
|
|
}
|
|
else if(s == "-nostdlib")
|
|
{
|
|
nostdlib = true;
|
|
}
|
|
else if(s.StartsWith("-lib:"))
|
|
{
|
|
libpaths.Add(s.Substring(5));
|
|
}
|
|
else if(s == "-bootstrap")
|
|
{
|
|
bootstrap = true;
|
|
}
|
|
else if(s.StartsWith("-out:"))
|
|
{
|
|
outputFile = s.Substring(5);
|
|
}
|
|
else if(s.StartsWith("-namespace:"))
|
|
{
|
|
namespaces.Add(s.Substring(11) + ".");
|
|
}
|
|
else if(s == "-forwarders")
|
|
{
|
|
forwarders = true;
|
|
}
|
|
else
|
|
{
|
|
// unrecognized option, or multiple assemblies, print usage message and exit
|
|
assemblyNameOrPath = null;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assemblyNameOrPath = s;
|
|
}
|
|
}
|
|
if(assemblyNameOrPath == null)
|
|
{
|
|
Console.Error.WriteLine(GetVersionAndCopyrightInfo());
|
|
Console.Error.WriteLine();
|
|
Console.Error.WriteLine("usage: ikvmstub [-options] <assemblyNameOrPath>");
|
|
Console.Error.WriteLine();
|
|
Console.Error.WriteLine("options:");
|
|
Console.Error.WriteLine(" -out:<outputfile> Specify the output filename");
|
|
Console.Error.WriteLine(" -reference:<filespec> Reference an assembly (short form -r:<filespec>)");
|
|
Console.Error.WriteLine(" -japi Generate jar suitable for comparison with japitools");
|
|
Console.Error.WriteLine(" -skiperror Continue when errors are encountered");
|
|
Console.Error.WriteLine(" -shared Process all assemblies in shared group");
|
|
Console.Error.WriteLine(" -nostdlib Do not reference standard libraries");
|
|
Console.Error.WriteLine(" -lib:<dir> Additional directories to search for references");
|
|
Console.Error.WriteLine(" -namespace:<ns> Only include types from specified namespace");
|
|
Console.Error.WriteLine(" -forwarders Export forwarded types too");
|
|
return 1;
|
|
}
|
|
if(File.Exists(assemblyNameOrPath) && nostdlib)
|
|
{
|
|
// Add the target assembly to the references list, to allow it to be considered as "mscorlib".
|
|
// This allows "ikvmstub -nostdlib \...\mscorlib.dll" to work.
|
|
references.Add(assemblyNameOrPath);
|
|
}
|
|
StaticCompiler.Resolver.Warning += new AssemblyResolver.WarningEvent(Resolver_Warning);
|
|
StaticCompiler.Resolver.Init(StaticCompiler.Universe, nostdlib, references, libpaths);
|
|
Dictionary<string, Assembly> cache = new Dictionary<string, Assembly>();
|
|
foreach (string reference in references)
|
|
{
|
|
Assembly[] dummy = null;
|
|
if (!StaticCompiler.Resolver.ResolveReference(cache, ref dummy, reference))
|
|
{
|
|
Console.Error.WriteLine("Error: reference not found {0}", reference);
|
|
return 1;
|
|
}
|
|
}
|
|
Assembly assembly = null;
|
|
try
|
|
{
|
|
file = new FileInfo(assemblyNameOrPath);
|
|
}
|
|
catch(System.Exception x)
|
|
{
|
|
Console.Error.WriteLine("Error: unable to load \"{0}\"\n {1}", assemblyNameOrPath, x.Message);
|
|
return 1;
|
|
}
|
|
if(file != null && file.Exists)
|
|
{
|
|
assembly = StaticCompiler.LoadFile(assemblyNameOrPath);
|
|
}
|
|
else
|
|
{
|
|
assembly = StaticCompiler.Resolver.LoadWithPartialName(assemblyNameOrPath);
|
|
}
|
|
int rc = 0;
|
|
if(assembly == null)
|
|
{
|
|
Console.Error.WriteLine("Error: Assembly \"{0}\" not found", assemblyNameOrPath);
|
|
}
|
|
else
|
|
{
|
|
if (bootstrap)
|
|
{
|
|
StaticCompiler.runtimeAssembly = StaticCompiler.LoadFile(typeof(NetExp).Assembly.Location);
|
|
ClassLoaderWrapper.SetBootstrapClassLoader(new BootstrapBootstrapClassLoader());
|
|
}
|
|
else
|
|
{
|
|
StaticCompiler.LoadFile(typeof(NetExp).Assembly.Location);
|
|
StaticCompiler.runtimeAssembly = StaticCompiler.LoadFile(Path.Combine(typeof(NetExp).Assembly.Location, "../IKVM.Runtime.dll"));
|
|
JVM.CoreAssembly = StaticCompiler.LoadFile(Path.Combine(typeof(NetExp).Assembly.Location, "../IKVM.OpenJDK.Core.dll"));
|
|
}
|
|
if (AttributeHelper.IsJavaModule(assembly.ManifestModule))
|
|
{
|
|
Console.Error.WriteLine("Warning: Running ikvmstub on ikvmc compiled assemblies is not supported.");
|
|
}
|
|
if (outputFile == null)
|
|
{
|
|
outputFile = assembly.GetName().Name + ".jar";
|
|
}
|
|
try
|
|
{
|
|
using (zipFile = new ZipOutputStream(new FileStream(outputFile, FileMode.Create)))
|
|
{
|
|
zipFile.SetComment(GetVersionAndCopyrightInfo());
|
|
try
|
|
{
|
|
List<Assembly> assemblies = new List<Assembly>();
|
|
assemblies.Add(assembly);
|
|
if (autoLoadSharedClassLoaderAssemblies)
|
|
{
|
|
LoadSharedClassLoaderAssemblies(assembly, assemblies);
|
|
}
|
|
foreach (Assembly asm in assemblies)
|
|
{
|
|
if (ProcessTypes(asm.GetTypes(), continueOnError) != 0)
|
|
{
|
|
rc = 1;
|
|
if (!continueOnError)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (forwarders && ProcessTypes(asm.ManifestModule.__GetExportedTypes(), continueOnError) != 0)
|
|
{
|
|
rc = 1;
|
|
if (!continueOnError)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (System.Exception x)
|
|
{
|
|
Console.Error.WriteLine(x);
|
|
|
|
if (!continueOnError)
|
|
{
|
|
Console.Error.WriteLine("Warning: Assembly reflection encountered an error. Resultant JAR may be incomplete.");
|
|
}
|
|
|
|
rc = 1;
|
|
}
|
|
}
|
|
}
|
|
catch (ZipException x)
|
|
{
|
|
rc = 1;
|
|
if (zipCount == 0)
|
|
{
|
|
Console.Error.WriteLine("Error: Assembly contains no public IKVM.NET compatible types");
|
|
}
|
|
else
|
|
{
|
|
Console.Error.WriteLine("Error: {0}", x.Message);
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static void Resolver_Warning(AssemblyResolver.WarningId warning, string message, string[] parameters)
|
|
{
|
|
if (warning != AssemblyResolver.WarningId.HigherVersion)
|
|
{
|
|
Console.Error.WriteLine("Warning: " + message, parameters);
|
|
}
|
|
}
|
|
|
|
private static string GetVersionAndCopyrightInfo()
|
|
{
|
|
System.Reflection.Assembly asm = System.Reflection.Assembly.GetEntryAssembly();
|
|
object[] desc = asm.GetCustomAttributes(typeof(System.Reflection.AssemblyTitleAttribute), false);
|
|
if (desc.Length == 1)
|
|
{
|
|
object[] copyright = asm.GetCustomAttributes(typeof(System.Reflection.AssemblyCopyrightAttribute), false);
|
|
if (copyright.Length == 1)
|
|
{
|
|
return string.Format("{0} version {1}{2}{3}{2}http://www.ikvm.net/",
|
|
((System.Reflection.AssemblyTitleAttribute)desc[0]).Title,
|
|
asm.GetName().Version,
|
|
Environment.NewLine,
|
|
((System.Reflection.AssemblyCopyrightAttribute)copyright[0]).Copyright);
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private static void LoadSharedClassLoaderAssemblies(Assembly assembly, List<Assembly> assemblies)
|
|
{
|
|
if (assembly.GetManifestResourceInfo("ikvm.exports") != null)
|
|
{
|
|
using (Stream stream = assembly.GetManifestResourceStream("ikvm.exports"))
|
|
{
|
|
BinaryReader rdr = new BinaryReader(stream);
|
|
int assemblyCount = rdr.ReadInt32();
|
|
for (int i = 0; i < assemblyCount; i++)
|
|
{
|
|
string name = rdr.ReadString();
|
|
int typeCount = rdr.ReadInt32();
|
|
if (typeCount > 0)
|
|
{
|
|
for (int j = 0; j < typeCount; j++)
|
|
{
|
|
rdr.ReadInt32();
|
|
}
|
|
try
|
|
{
|
|
assemblies.Add(StaticCompiler.Load(name));
|
|
}
|
|
catch
|
|
{
|
|
Console.WriteLine("Warning: Unable to load shared class loader assembly: {0}", name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void WriteClass(TypeWrapper tw)
|
|
{
|
|
string name = tw.Name.Replace('.', '/');
|
|
string super = null;
|
|
if (tw.IsInterface)
|
|
{
|
|
super = "java/lang/Object";
|
|
}
|
|
else if (tw.BaseTypeWrapper != null)
|
|
{
|
|
super = tw.BaseTypeWrapper.Name.Replace('.', '/');
|
|
}
|
|
IKVM.StubGen.ClassFileWriter writer = new IKVM.StubGen.ClassFileWriter(tw.Modifiers, name, super, 0, 49);
|
|
foreach (TypeWrapper iface in tw.Interfaces)
|
|
{
|
|
if (iface.IsPublic || includeNonPublicInterfaces)
|
|
{
|
|
writer.AddInterface(iface.Name.Replace('.', '/'));
|
|
}
|
|
}
|
|
IKVM.StubGen.InnerClassesAttribute innerClassesAttribute = null;
|
|
if (tw.DeclaringTypeWrapper != null)
|
|
{
|
|
TypeWrapper outer = tw.DeclaringTypeWrapper;
|
|
string innername = name;
|
|
int idx = name.LastIndexOf('$');
|
|
if (idx >= 0)
|
|
{
|
|
innername = innername.Substring(idx + 1);
|
|
}
|
|
innerClassesAttribute = new IKVM.StubGen.InnerClassesAttribute(writer);
|
|
innerClassesAttribute.Add(name, outer.Name.Replace('.', '/'), innername, (ushort)tw.ReflectiveModifiers);
|
|
}
|
|
foreach (TypeWrapper inner in tw.InnerClasses)
|
|
{
|
|
if (inner.IsPublic)
|
|
{
|
|
if (innerClassesAttribute == null)
|
|
{
|
|
innerClassesAttribute = new IKVM.StubGen.InnerClassesAttribute(writer);
|
|
}
|
|
string namePart = inner.Name;
|
|
namePart = namePart.Substring(namePart.LastIndexOf('$') + 1);
|
|
innerClassesAttribute.Add(inner.Name.Replace('.', '/'), name, namePart, (ushort)inner.ReflectiveModifiers);
|
|
}
|
|
}
|
|
if (innerClassesAttribute != null)
|
|
{
|
|
writer.AddAttribute(innerClassesAttribute);
|
|
}
|
|
string genericTypeSignature = tw.GetGenericSignature();
|
|
if (genericTypeSignature != null)
|
|
{
|
|
writer.AddStringAttribute("Signature", genericTypeSignature);
|
|
}
|
|
writer.AddStringAttribute("IKVM.NET.Assembly", GetAssemblyName(tw));
|
|
if (tw.TypeAsBaseType.IsDefined(StaticCompiler.Universe.Import(typeof(ObsoleteAttribute)), false))
|
|
{
|
|
writer.AddAttribute(new IKVM.StubGen.DeprecatedAttribute(writer));
|
|
}
|
|
foreach (MethodWrapper mw in tw.GetMethods())
|
|
{
|
|
if (!mw.IsHideFromReflection && (mw.IsPublic || mw.IsProtected || includeNonPublicMembers))
|
|
{
|
|
IKVM.StubGen.FieldOrMethod m;
|
|
if (mw.Name == "<init>")
|
|
{
|
|
m = writer.AddMethod(mw.Modifiers, mw.Name, mw.Signature.Replace('.', '/'));
|
|
IKVM.StubGen.CodeAttribute code = new IKVM.StubGen.CodeAttribute(writer);
|
|
code.MaxLocals = (ushort)(mw.GetParameters().Length * 2 + 1);
|
|
code.MaxStack = 3;
|
|
ushort index1 = writer.AddClass("java/lang/UnsatisfiedLinkError");
|
|
ushort index2 = writer.AddString("ikvmstub generated stubs can only be used on IKVM.NET");
|
|
ushort index3 = writer.AddMethodRef("java/lang/UnsatisfiedLinkError", "<init>", "(Ljava/lang/String;)V");
|
|
code.ByteCode = new byte[] {
|
|
187, (byte)(index1 >> 8), (byte)index1, // new java/lang/UnsatisfiedLinkError
|
|
89, // dup
|
|
19, (byte)(index2 >> 8), (byte)index2, // ldc_w "..."
|
|
183, (byte)(index3 >> 8), (byte)index3, // invokespecial java/lang/UnsatisfiedLinkError/init()V
|
|
191 // athrow
|
|
};
|
|
m.AddAttribute(code);
|
|
}
|
|
else
|
|
{
|
|
Modifiers mods = mw.Modifiers;
|
|
if ((mods & Modifiers.Abstract) == 0)
|
|
{
|
|
mods |= Modifiers.Native;
|
|
}
|
|
m = writer.AddMethod(mods, mw.Name, mw.Signature.Replace('.', '/'));
|
|
if (mw.IsOptionalAttributeAnnotationValue)
|
|
{
|
|
m.AddAttribute(new IKVM.StubGen.AnnotationDefaultClassFileAttribute(writer, GetAnnotationDefault(writer, mw.ReturnType)));
|
|
}
|
|
}
|
|
MethodBase mb = mw.GetMethod();
|
|
if (mb != null)
|
|
{
|
|
ThrowsAttribute throws = AttributeHelper.GetThrows(mb);
|
|
if (throws == null)
|
|
{
|
|
string[] throwsArray = mw.GetDeclaredExceptions();
|
|
if (throwsArray != null && throwsArray.Length > 0)
|
|
{
|
|
IKVM.StubGen.ExceptionsAttribute attrib = new IKVM.StubGen.ExceptionsAttribute(writer);
|
|
foreach (string ex in throwsArray)
|
|
{
|
|
attrib.Add(ex.Replace('.', '/'));
|
|
}
|
|
m.AddAttribute(attrib);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IKVM.StubGen.ExceptionsAttribute attrib = new IKVM.StubGen.ExceptionsAttribute(writer);
|
|
if (throws.classes != null)
|
|
{
|
|
foreach (string ex in throws.classes)
|
|
{
|
|
attrib.Add(ex.Replace('.', '/'));
|
|
}
|
|
}
|
|
if (throws.types != null)
|
|
{
|
|
foreach (Type ex in throws.types)
|
|
{
|
|
attrib.Add(ClassLoaderWrapper.GetWrapperFromType(ex).Name.Replace('.', '/'));
|
|
}
|
|
}
|
|
m.AddAttribute(attrib);
|
|
}
|
|
if (mb.IsDefined(StaticCompiler.Universe.Import(typeof(ObsoleteAttribute)), false)
|
|
// HACK the instancehelper methods are marked as Obsolete (to direct people toward the ikvm.extensions methods instead)
|
|
// but in the Java world most of them are not deprecated (and to keep the Japi results clean we need to reflect this)
|
|
&& (!mb.Name.StartsWith("instancehelper_")
|
|
|| mb.DeclaringType.FullName != "java.lang.String"
|
|
// the Java deprecated methods actually have two Obsolete attributes
|
|
|| mb.__GetCustomAttributes(StaticCompiler.Universe.Import(typeof(ObsoleteAttribute)), false).Count == 2))
|
|
{
|
|
m.AddAttribute(new IKVM.StubGen.DeprecatedAttribute(writer));
|
|
}
|
|
IList<CustomAttributeData> attr = CustomAttributeData.__GetCustomAttributes(mb, JVM.LoadType(typeof(AnnotationDefaultAttribute)), false);
|
|
if (attr.Count == 1)
|
|
{
|
|
m.AddAttribute(new IKVM.StubGen.AnnotationDefaultClassFileAttribute(writer, GetAnnotationDefault(writer, attr[0].ConstructorArguments[0])));
|
|
}
|
|
}
|
|
string sig = tw.GetGenericMethodSignature(mw);
|
|
if (sig != null)
|
|
{
|
|
m.AddAttribute(writer.MakeStringAttribute("Signature", sig));
|
|
}
|
|
}
|
|
}
|
|
bool hasSerialVersionUID = false;
|
|
foreach (FieldWrapper fw in tw.GetFields())
|
|
{
|
|
if (!fw.IsHideFromReflection)
|
|
{
|
|
bool isSerialVersionUID = includeSerialVersionUID && fw.Name == "serialVersionUID" && fw.FieldTypeWrapper == PrimitiveTypeWrapper.LONG;
|
|
hasSerialVersionUID |= isSerialVersionUID;
|
|
if (fw.IsPublic || fw.IsProtected || isSerialVersionUID || includeNonPublicMembers)
|
|
{
|
|
object constant = null;
|
|
if (fw.GetField() != null && fw.GetField().IsLiteral && (fw.FieldTypeWrapper.IsPrimitive || fw.FieldTypeWrapper == CoreClasses.java.lang.String.Wrapper))
|
|
{
|
|
constant = fw.GetField().GetRawConstantValue();
|
|
if (fw.GetField().FieldType.IsEnum)
|
|
{
|
|
constant = EnumHelper.GetPrimitiveValue(EnumHelper.GetUnderlyingType(fw.GetField().FieldType), constant);
|
|
}
|
|
}
|
|
IKVM.StubGen.FieldOrMethod f = writer.AddField(fw.Modifiers, fw.Name, fw.Signature.Replace('.', '/'), constant);
|
|
string sig = tw.GetGenericFieldSignature(fw);
|
|
if (sig != null)
|
|
{
|
|
f.AddAttribute(writer.MakeStringAttribute("Signature", sig));
|
|
}
|
|
if (fw.GetField() != null && fw.GetField().IsDefined(StaticCompiler.Universe.Import(typeof(ObsoleteAttribute)), false))
|
|
{
|
|
f.AddAttribute(new IKVM.StubGen.DeprecatedAttribute(writer));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (includeSerialVersionUID && !hasSerialVersionUID && IsSerializable(tw))
|
|
{
|
|
// class is serializable but doesn't have an explicit serialVersionUID, so we add the field to record
|
|
// the serialVersionUID as we see it (mainly to make the Japi reports more realistic)
|
|
writer.AddField(Modifiers.Private | Modifiers.Static | Modifiers.Final, "serialVersionUID", "J", IKVM.StubGen.SerialVersionUID.Compute(tw));
|
|
}
|
|
AddMetaAnnotations(writer, tw);
|
|
zipCount++;
|
|
MemoryStream mem = new MemoryStream();
|
|
writer.Write(mem);
|
|
ZipEntry entry = new ZipEntry(name + ".class");
|
|
entry.Size = mem.Position;
|
|
zipFile.PutNextEntry(entry);
|
|
mem.WriteTo(zipFile);
|
|
}
|
|
|
|
private static string GetAssemblyName(TypeWrapper tw)
|
|
{
|
|
ClassLoaderWrapper loader = tw.GetClassLoader();
|
|
AssemblyClassLoader acl = loader as AssemblyClassLoader;
|
|
if (acl != null)
|
|
{
|
|
return acl.GetAssembly(tw).FullName;
|
|
}
|
|
else
|
|
{
|
|
return ((GenericClassLoader)loader).GetName();
|
|
}
|
|
}
|
|
|
|
private static bool IsSerializable(TypeWrapper tw)
|
|
{
|
|
if (tw.Name == "java.io.Serializable")
|
|
{
|
|
return true;
|
|
}
|
|
while (tw != null)
|
|
{
|
|
foreach (TypeWrapper iface in tw.Interfaces)
|
|
{
|
|
if (IsSerializable(iface))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
tw = tw.BaseTypeWrapper;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static void AddMetaAnnotations(IKVM.StubGen.ClassFileWriter writer, TypeWrapper tw)
|
|
{
|
|
DotNetTypeWrapper.AttributeAnnotationTypeWrapperBase attributeAnnotation = tw as DotNetTypeWrapper.AttributeAnnotationTypeWrapperBase;
|
|
if (attributeAnnotation != null)
|
|
{
|
|
// TODO write the annotation directly, instead of going thru the object[] encoding
|
|
IKVM.StubGen.RuntimeVisibleAnnotationsAttribute annot = new IKVM.StubGen.RuntimeVisibleAnnotationsAttribute(writer);
|
|
annot.Add(new object[] {
|
|
AnnotationDefaultAttribute.TAG_ANNOTATION,
|
|
"Ljava/lang/annotation/Retention;",
|
|
"value",
|
|
new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME" }
|
|
});
|
|
AttributeTargets validOn = attributeAnnotation.AttributeTargets;
|
|
List<object[]> targets = new List<object[]>();
|
|
if ((validOn & (AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate | AttributeTargets.Assembly)) != 0)
|
|
{
|
|
targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "TYPE" });
|
|
}
|
|
if ((validOn & AttributeTargets.Constructor) != 0)
|
|
{
|
|
targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "CONSTRUCTOR" });
|
|
}
|
|
if ((validOn & AttributeTargets.Field) != 0)
|
|
{
|
|
targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "FIELD" });
|
|
}
|
|
if ((validOn & (AttributeTargets.Method | AttributeTargets.ReturnValue)) != 0)
|
|
{
|
|
targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "METHOD" });
|
|
}
|
|
if ((validOn & AttributeTargets.Parameter) != 0)
|
|
{
|
|
targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "PARAMETER" });
|
|
}
|
|
annot.Add(new object[] {
|
|
AnnotationDefaultAttribute.TAG_ANNOTATION,
|
|
"Ljava/lang/annotation/Target;",
|
|
"value",
|
|
new object[] { AnnotationDefaultAttribute.TAG_ARRAY, targets.ToArray() }
|
|
});
|
|
writer.AddAttribute(annot);
|
|
}
|
|
}
|
|
|
|
private static byte[] GetAnnotationDefault(IKVM.StubGen.ClassFileWriter classFile, TypeWrapper type)
|
|
{
|
|
MemoryStream mem = new MemoryStream();
|
|
IKVM.StubGen.BigEndianStream bes = new IKVM.StubGen.BigEndianStream(mem);
|
|
if (type == PrimitiveTypeWrapper.BOOLEAN)
|
|
{
|
|
bes.WriteByte((byte)'Z');
|
|
bes.WriteUInt16(classFile.AddInt(0));
|
|
}
|
|
else if(type == PrimitiveTypeWrapper.BYTE)
|
|
{
|
|
bes.WriteByte((byte)'B');
|
|
bes.WriteUInt16(classFile.AddInt(0));
|
|
}
|
|
else if(type == PrimitiveTypeWrapper.CHAR)
|
|
{
|
|
bes.WriteByte((byte)'C');
|
|
bes.WriteUInt16(classFile.AddInt(0));
|
|
}
|
|
else if(type == PrimitiveTypeWrapper.SHORT)
|
|
{
|
|
bes.WriteByte((byte)'S');
|
|
bes.WriteUInt16(classFile.AddInt(0));
|
|
}
|
|
else if(type == PrimitiveTypeWrapper.INT)
|
|
{
|
|
bes.WriteByte((byte)'I');
|
|
bes.WriteUInt16(classFile.AddInt(0));
|
|
}
|
|
else if(type == PrimitiveTypeWrapper.FLOAT)
|
|
{
|
|
bes.WriteByte((byte)'F');
|
|
bes.WriteUInt16(classFile.AddFloat(0));
|
|
}
|
|
else if(type == PrimitiveTypeWrapper.LONG)
|
|
{
|
|
bes.WriteByte((byte)'J');
|
|
bes.WriteUInt16(classFile.AddLong(0));
|
|
}
|
|
else if (type == PrimitiveTypeWrapper.DOUBLE)
|
|
{
|
|
bes.WriteByte((byte)'D');
|
|
bes.WriteUInt16(classFile.AddDouble(0));
|
|
}
|
|
else if (type == CoreClasses.java.lang.String.Wrapper)
|
|
{
|
|
bes.WriteByte((byte)'s');
|
|
bes.WriteUInt16(classFile.AddUtf8(""));
|
|
}
|
|
else if ((type.Modifiers & Modifiers.Enum) != 0)
|
|
{
|
|
bes.WriteByte((byte)'e');
|
|
bes.WriteUInt16(classFile.AddUtf8("L" + type.Name.Replace('.', '/') + ";"));
|
|
bes.WriteUInt16(classFile.AddUtf8("__unspecified"));
|
|
}
|
|
else if (type == CoreClasses.java.lang.Class.Wrapper)
|
|
{
|
|
bes.WriteByte((byte)'c');
|
|
bes.WriteUInt16(classFile.AddUtf8("Likvm/internal/__unspecified;"));
|
|
}
|
|
else if (type.IsArray)
|
|
{
|
|
bes.WriteByte((byte)'[');
|
|
bes.WriteUInt16(0);
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
return mem.ToArray();
|
|
}
|
|
|
|
private static byte[] GetAnnotationDefault(IKVM.StubGen.ClassFileWriter classFile, CustomAttributeTypedArgument value)
|
|
{
|
|
MemoryStream mem = new MemoryStream();
|
|
IKVM.StubGen.BigEndianStream bes = new IKVM.StubGen.BigEndianStream(mem);
|
|
try
|
|
{
|
|
WriteAnnotationElementValue(classFile, bes, value);
|
|
}
|
|
catch (InvalidCastException)
|
|
{
|
|
Console.Error.WriteLine("Warning: incorrect annotation default value");
|
|
}
|
|
catch (IndexOutOfRangeException)
|
|
{
|
|
Console.Error.WriteLine("Warning: incorrect annotation default value");
|
|
}
|
|
return mem.ToArray();
|
|
}
|
|
|
|
private static void WriteAnnotationElementValue(IKVM.StubGen.ClassFileWriter classFile, IKVM.StubGen.BigEndianStream bes, CustomAttributeTypedArgument value)
|
|
{
|
|
if (value.ArgumentType == Types.Boolean)
|
|
{
|
|
bes.WriteByte((byte)'Z');
|
|
bes.WriteUInt16(classFile.AddInt((bool)value.Value ? 1 : 0));
|
|
}
|
|
else if (value.ArgumentType == Types.Byte)
|
|
{
|
|
bes.WriteByte((byte)'B');
|
|
bes.WriteUInt16(classFile.AddInt((byte)value.Value));
|
|
}
|
|
else if (value.ArgumentType == Types.Char)
|
|
{
|
|
bes.WriteByte((byte)'C');
|
|
bes.WriteUInt16(classFile.AddInt((char)value.Value));
|
|
}
|
|
else if (value.ArgumentType == Types.Int16)
|
|
{
|
|
bes.WriteByte((byte)'S');
|
|
bes.WriteUInt16(classFile.AddInt((short)value.Value));
|
|
}
|
|
else if (value.ArgumentType == Types.Int32)
|
|
{
|
|
bes.WriteByte((byte)'I');
|
|
bes.WriteUInt16(classFile.AddInt((int)value.Value));
|
|
}
|
|
else if (value.ArgumentType == Types.Single)
|
|
{
|
|
bes.WriteByte((byte)'F');
|
|
bes.WriteUInt16(classFile.AddFloat((float)value.Value));
|
|
}
|
|
else if (value.ArgumentType == Types.Int64)
|
|
{
|
|
bes.WriteByte((byte)'J');
|
|
bes.WriteUInt16(classFile.AddLong((long)value.Value));
|
|
}
|
|
else if (value.ArgumentType == Types.Double)
|
|
{
|
|
bes.WriteByte((byte)'D');
|
|
bes.WriteUInt16(classFile.AddDouble((double)value.Value));
|
|
}
|
|
else if (value.ArgumentType == Types.String)
|
|
{
|
|
bes.WriteByte((byte)'s');
|
|
bes.WriteUInt16(classFile.AddUtf8((string)value.Value));
|
|
}
|
|
else if (value.ArgumentType == Types.Object.MakeArrayType())
|
|
{
|
|
CustomAttributeTypedArgument[] array = (CustomAttributeTypedArgument[])value.Value;
|
|
byte type = (byte)array[0].Value;
|
|
if (type == AnnotationDefaultAttribute.TAG_ARRAY)
|
|
{
|
|
bes.WriteByte((byte)'[');
|
|
bes.WriteUInt16((ushort)(array.Length - 1));
|
|
for (int i = 1; i < array.Length; i++)
|
|
{
|
|
WriteAnnotationElementValue(classFile, bes, array[i]);
|
|
}
|
|
}
|
|
else if (type == AnnotationDefaultAttribute.TAG_CLASS)
|
|
{
|
|
bes.WriteByte((byte)'c');
|
|
bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value));
|
|
}
|
|
else if (type == AnnotationDefaultAttribute.TAG_ENUM)
|
|
{
|
|
bes.WriteByte((byte)'e');
|
|
bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value));
|
|
bes.WriteUInt16(classFile.AddUtf8((string)array[2].Value));
|
|
}
|
|
else if (type == AnnotationDefaultAttribute.TAG_ANNOTATION)
|
|
{
|
|
bes.WriteByte((byte)'@');
|
|
bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value));
|
|
bes.WriteUInt16((ushort)((array.Length - 2) / 2));
|
|
for (int i = 2; i < array.Length; i += 2)
|
|
{
|
|
bes.WriteUInt16(classFile.AddUtf8((string)array[i].Value));
|
|
WriteAnnotationElementValue(classFile, bes, array[i + 1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.Error.WriteLine("Warning: incorrect annotation default element tag: " + type);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.Error.WriteLine("Warning: incorrect annotation default element type: " + value.ArgumentType);
|
|
}
|
|
}
|
|
|
|
private static bool ExportNamespace(Type type)
|
|
{
|
|
if (namespaces.Count == 0)
|
|
{
|
|
return true;
|
|
}
|
|
string name = type.FullName;
|
|
foreach (string ns in namespaces)
|
|
{
|
|
if (name.StartsWith(ns, StringComparison.Ordinal))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static int ProcessTypes(Type[] types, bool continueOnError)
|
|
{
|
|
int rc = 0;
|
|
foreach (Type t in types)
|
|
{
|
|
if (t.IsPublic
|
|
&& ExportNamespace(t)
|
|
&& !t.IsGenericTypeDefinition
|
|
&& !AttributeHelper.IsHideFromJava(t)
|
|
&& (!t.IsGenericType || !AttributeHelper.IsJavaModule(t.Module)))
|
|
{
|
|
TypeWrapper c;
|
|
if (ClassLoaderWrapper.IsRemappedType(t) || t.IsPrimitive || t == Types.Void)
|
|
{
|
|
c = DotNetTypeWrapper.GetWrapperFromDotNetType(t);
|
|
}
|
|
else
|
|
{
|
|
c = ClassLoaderWrapper.GetWrapperFromType(t);
|
|
}
|
|
if (c != null)
|
|
{
|
|
AddToExportList(c);
|
|
}
|
|
}
|
|
}
|
|
bool keepGoing;
|
|
do
|
|
{
|
|
keepGoing = false;
|
|
foreach (TypeWrapper c in new List<TypeWrapper>(todo.Values))
|
|
{
|
|
if(!done.ContainsKey(c.Name))
|
|
{
|
|
keepGoing = true;
|
|
done.Add(c.Name, null);
|
|
|
|
try
|
|
{
|
|
ProcessClass(c);
|
|
}
|
|
catch (Exception x)
|
|
{
|
|
if (continueOnError)
|
|
{
|
|
rc = 1;
|
|
Console.WriteLine(x);
|
|
}
|
|
else
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
WriteClass(c);
|
|
}
|
|
}
|
|
} while(keepGoing);
|
|
return rc;
|
|
}
|
|
|
|
private static void AddToExportList(TypeWrapper c)
|
|
{
|
|
todo[c.Name] = c;
|
|
}
|
|
|
|
private static bool IsNonVectorArray(TypeWrapper tw)
|
|
{
|
|
return !tw.IsArray && tw.TypeAsBaseType.IsArray;
|
|
}
|
|
|
|
private static void AddToExportListIfNeeded(TypeWrapper tw)
|
|
{
|
|
while (tw.IsArray)
|
|
{
|
|
tw = tw.ElementTypeWrapper;
|
|
}
|
|
if (tw.IsUnloadable && tw.Name.StartsWith("Missing/"))
|
|
{
|
|
Console.Error.WriteLine("Error: unable to find assembly '{0}'", tw.Name.Substring(8));
|
|
Environment.Exit(1);
|
|
return;
|
|
}
|
|
if (tw is StubTypeWrapper)
|
|
{
|
|
// skip
|
|
}
|
|
else if ((tw.TypeAsTBD != null && tw.TypeAsTBD.IsGenericType) || IsNonVectorArray(tw) || !tw.IsPublic)
|
|
{
|
|
AddToExportList(tw);
|
|
}
|
|
}
|
|
|
|
private static void AddToExportListIfNeeded(TypeWrapper[] types)
|
|
{
|
|
foreach (TypeWrapper tw in types)
|
|
{
|
|
AddToExportListIfNeeded(tw);
|
|
}
|
|
}
|
|
|
|
private static void ProcessClass(TypeWrapper tw)
|
|
{
|
|
TypeWrapper superclass = tw.BaseTypeWrapper;
|
|
if (superclass != null)
|
|
{
|
|
AddToExportListIfNeeded(superclass);
|
|
}
|
|
AddToExportListIfNeeded(tw.Interfaces);
|
|
TypeWrapper outerClass = tw.DeclaringTypeWrapper;
|
|
if (outerClass != null)
|
|
{
|
|
AddToExportList(outerClass);
|
|
}
|
|
foreach (TypeWrapper innerClass in tw.InnerClasses)
|
|
{
|
|
if (innerClass.IsPublic)
|
|
{
|
|
AddToExportList(innerClass);
|
|
}
|
|
}
|
|
foreach (MethodWrapper mw in tw.GetMethods())
|
|
{
|
|
if (mw.IsPublic || mw.IsProtected)
|
|
{
|
|
mw.Link();
|
|
AddToExportListIfNeeded(mw.ReturnType);
|
|
AddToExportListIfNeeded(mw.GetParameters());
|
|
}
|
|
}
|
|
foreach (FieldWrapper fw in tw.GetFields())
|
|
{
|
|
if (fw.IsPublic || fw.IsProtected)
|
|
{
|
|
fw.Link();
|
|
AddToExportListIfNeeded(fw.FieldTypeWrapper);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static class Intrinsics
|
|
{
|
|
internal static bool IsIntrinsic(MethodWrapper methodWrapper)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static class StaticCompiler
|
|
{
|
|
internal static readonly Universe Universe = new Universe();
|
|
internal static readonly AssemblyResolver Resolver = new AssemblyResolver();
|
|
internal static Assembly runtimeAssembly;
|
|
|
|
internal static Type GetRuntimeType(string typeName)
|
|
{
|
|
return runtimeAssembly.GetType(typeName, true);
|
|
}
|
|
|
|
internal static Assembly LoadFile(string fileName)
|
|
{
|
|
return Resolver.LoadFile(fileName);
|
|
}
|
|
|
|
internal static Assembly Load(string name)
|
|
{
|
|
return Universe.Load(name);
|
|
}
|
|
}
|
|
|
|
static class FakeTypes
|
|
{
|
|
private static readonly Type genericType;
|
|
|
|
class Holder<T> { }
|
|
|
|
static FakeTypes()
|
|
{
|
|
genericType = StaticCompiler.Universe.Import(typeof(Holder<>));
|
|
}
|
|
|
|
internal static Type GetAttributeType(Type type)
|
|
{
|
|
return genericType.MakeGenericType(type);
|
|
}
|
|
|
|
internal static Type GetAttributeReturnValueType(Type type)
|
|
{
|
|
return genericType.MakeGenericType(type);
|
|
}
|
|
|
|
internal static Type GetAttributeMultipleType(Type type)
|
|
{
|
|
return genericType.MakeGenericType(type);
|
|
}
|
|
|
|
internal static Type GetDelegateType(Type type)
|
|
{
|
|
return genericType.MakeGenericType(type);
|
|
}
|
|
|
|
internal static Type GetEnumType(Type type)
|
|
{
|
|
return genericType.MakeGenericType(type);
|
|
}
|
|
}
|
|
|
|
sealed class BootstrapBootstrapClassLoader : ClassLoaderWrapper
|
|
{
|
|
internal BootstrapBootstrapClassLoader()
|
|
: base(CodeGenOptions.None, null)
|
|
{
|
|
TypeWrapper javaLangObject = new StubTypeWrapper(Modifiers.Public, "java.lang.Object", null, true);
|
|
SetRemappedType(JVM.Import(typeof(object)), javaLangObject);
|
|
SetRemappedType(JVM.Import(typeof(string)), new StubTypeWrapper(Modifiers.Public | Modifiers.Final, "java.lang.String", javaLangObject, true));
|
|
SetRemappedType(JVM.Import(typeof(Exception)), new StubTypeWrapper(Modifiers.Public, "java.lang.Throwable", javaLangObject, true));
|
|
SetRemappedType(JVM.Import(typeof(IComparable)), new StubTypeWrapper(Modifiers.Public | Modifiers.Abstract | Modifiers.Interface, "java.lang.Comparable", null, true));
|
|
TypeWrapper tw = new StubTypeWrapper(Modifiers.Public | Modifiers.Abstract | Modifiers.Interface, "java.lang.AutoCloseable", null, true);
|
|
tw.SetMethods(new MethodWrapper[] { new SimpleCallMethodWrapper(tw, "close", "()V", JVM.Import(typeof(IDisposable)).GetMethod("Dispose"), PrimitiveTypeWrapper.VOID, TypeWrapper.EmptyArray, Modifiers.Public | Modifiers.Abstract, MemberFlags.None, SimpleOpCode.Callvirt, SimpleOpCode.Callvirt) });
|
|
SetRemappedType(JVM.Import(typeof(IDisposable)), tw);
|
|
|
|
RegisterInitiatingLoader(new StubTypeWrapper(Modifiers.Public, "java.lang.Enum", javaLangObject, false));
|
|
RegisterInitiatingLoader(new StubTypeWrapper(Modifiers.Public | Modifiers.Abstract | Modifiers.Interface, "java.lang.annotation.Annotation", null, false));
|
|
RegisterInitiatingLoader(new StubTypeWrapper(Modifiers.Public | Modifiers.Final, "java.lang.Class", javaLangObject, false));
|
|
}
|
|
}
|
|
|
|
sealed class StubTypeWrapper : TypeWrapper
|
|
{
|
|
private readonly bool remapped;
|
|
private readonly TypeWrapper baseWrapper;
|
|
|
|
internal StubTypeWrapper(Modifiers modifiers, string name, TypeWrapper baseWrapper, bool remapped)
|
|
: base(modifiers, name)
|
|
{
|
|
this.remapped = remapped;
|
|
this.baseWrapper = baseWrapper;
|
|
}
|
|
|
|
internal override TypeWrapper BaseTypeWrapper
|
|
{
|
|
get { return baseWrapper; }
|
|
}
|
|
|
|
internal override ClassLoaderWrapper GetClassLoader()
|
|
{
|
|
return ClassLoaderWrapper.GetBootstrapClassLoader();
|
|
}
|
|
|
|
internal override Type TypeAsTBD
|
|
{
|
|
get { throw new NotSupportedException(); }
|
|
}
|
|
|
|
internal override TypeWrapper[] Interfaces
|
|
{
|
|
get { return TypeWrapper.EmptyArray; }
|
|
}
|
|
|
|
internal override TypeWrapper[] InnerClasses
|
|
{
|
|
get { return TypeWrapper.EmptyArray; }
|
|
}
|
|
|
|
internal override TypeWrapper DeclaringTypeWrapper
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
internal override void Finish()
|
|
{
|
|
}
|
|
|
|
internal override bool IsRemapped
|
|
{
|
|
get { return remapped; }
|
|
}
|
|
}
|