2014-08-13 10:39:27 +01:00
|
|
|
/*
|
2014-10-04 11:27:48 +01:00
|
|
|
Copyright (C) 2002-2014 Jeroen Frijters
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
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 IKVM.Attributes;
|
|
|
|
|
|
|
|
namespace IKVM.Internal
|
|
|
|
{
|
|
|
|
enum HardError : short
|
|
|
|
{
|
|
|
|
NoClassDefFoundError,
|
|
|
|
IllegalAccessError,
|
|
|
|
InstantiationError,
|
|
|
|
IncompatibleClassChangeError,
|
|
|
|
NoSuchFieldError,
|
|
|
|
AbstractMethodError,
|
|
|
|
NoSuchMethodError,
|
|
|
|
LinkageError,
|
|
|
|
// "exceptions" that are wrapped in an IncompatibleClassChangeError
|
|
|
|
IllegalAccessException,
|
|
|
|
// if an error is added here, it must also be added to MethodAnalyzer.SetHardError()
|
|
|
|
}
|
|
|
|
|
|
|
|
[Flags]
|
|
|
|
enum ClassFileParseOptions
|
|
|
|
{
|
|
|
|
None = 0,
|
|
|
|
LocalVariableTable = 1,
|
|
|
|
LineNumberTable = 2,
|
|
|
|
RelaxedClassNameValidation = 4,
|
2014-10-04 11:27:48 +01:00
|
|
|
TrustedAnnotations = 8,
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sealed class ClassFile
|
|
|
|
{
|
|
|
|
private ConstantPoolItem[] constantpool;
|
|
|
|
// Modifiers is a ushort, so the next four fields combine into two 32 bit slots
|
|
|
|
private Modifiers access_flags;
|
|
|
|
private ushort this_class;
|
|
|
|
private ushort super_class;
|
|
|
|
private ushort flags;
|
|
|
|
private const ushort FLAG_MASK_MAJORVERSION = 0xFF;
|
|
|
|
private const ushort FLAG_MASK_DEPRECATED = 0x100;
|
|
|
|
private const ushort FLAG_MASK_INTERNAL = 0x200;
|
|
|
|
private const ushort FLAG_CALLERSENSITIVE = 0x400;
|
2014-10-04 11:27:48 +01:00
|
|
|
private const ushort FLAG_LAMBDAFORM_COMPILED = 0x800;
|
|
|
|
private const ushort FLAG_LAMBDAFORM_HIDDEN = 0x1000;
|
|
|
|
private const ushort FLAG_FORCEINLINE = 0x2000;
|
2014-08-13 10:39:27 +01:00
|
|
|
private ConstantPoolItemClass[] interfaces;
|
|
|
|
private Field[] fields;
|
|
|
|
private Method[] methods;
|
|
|
|
private string sourceFile;
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
private string sourcePath;
|
|
|
|
#endif
|
|
|
|
private string ikvmAssembly;
|
|
|
|
private InnerClass[] innerClasses;
|
|
|
|
private object[] annotations;
|
|
|
|
private string signature;
|
|
|
|
private string[] enclosingMethod;
|
|
|
|
private BootstrapMethod[] bootstrapMethods;
|
2014-10-04 11:27:48 +01:00
|
|
|
private byte[] runtimeVisibleTypeAnnotations;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
private static class SupportedVersions
|
|
|
|
{
|
|
|
|
internal static readonly int Minimum = 45;
|
2014-10-04 11:27:48 +01:00
|
|
|
internal static readonly int Maximum = Experimental.JDK_9 ? 53 : 52;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
// This method parses just enough of the class file to obtain its name and
|
|
|
|
// determine if the class is a possible ikvmstub generated stub, it doesn't
|
|
|
|
// validate the class file structure, but it may throw a ClassFormatError if it
|
|
|
|
// encounters bogus data
|
|
|
|
internal static string GetClassName(byte[] buf, int offset, int length, out bool isstub)
|
|
|
|
{
|
|
|
|
isstub = false;
|
|
|
|
BigEndianBinaryReader br = new BigEndianBinaryReader(buf, offset, length);
|
|
|
|
if(br.ReadUInt32() != 0xCAFEBABE)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Bad magic number");
|
|
|
|
}
|
|
|
|
int minorVersion = br.ReadUInt16();
|
|
|
|
int majorVersion = br.ReadUInt16();
|
|
|
|
if((majorVersion & FLAG_MASK_MAJORVERSION) != majorVersion
|
|
|
|
|| majorVersion < SupportedVersions.Minimum
|
|
|
|
|| majorVersion > SupportedVersions.Maximum
|
|
|
|
|| (majorVersion == SupportedVersions.Minimum && minorVersion < 3)
|
|
|
|
|| (majorVersion == SupportedVersions.Maximum && minorVersion != 0))
|
|
|
|
{
|
|
|
|
throw new UnsupportedClassVersionError(majorVersion + "." + minorVersion);
|
|
|
|
}
|
|
|
|
int constantpoolcount = br.ReadUInt16();
|
|
|
|
int[] cpclass = new int[constantpoolcount];
|
|
|
|
string[] utf8_cp = new string[constantpoolcount];
|
|
|
|
for(int i = 1; i < constantpoolcount; i++)
|
|
|
|
{
|
|
|
|
Constant tag = (Constant)br.ReadByte();
|
|
|
|
switch(tag)
|
|
|
|
{
|
|
|
|
case Constant.Class:
|
|
|
|
cpclass[i] = br.ReadUInt16();
|
|
|
|
break;
|
|
|
|
case Constant.Double:
|
|
|
|
case Constant.Long:
|
|
|
|
br.Skip(8);
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
case Constant.Fieldref:
|
|
|
|
case Constant.InterfaceMethodref:
|
|
|
|
case Constant.Methodref:
|
|
|
|
case Constant.InvokeDynamic:
|
|
|
|
case Constant.NameAndType:
|
|
|
|
case Constant.Float:
|
|
|
|
case Constant.Integer:
|
|
|
|
br.Skip(4);
|
|
|
|
break;
|
|
|
|
case Constant.MethodHandle:
|
|
|
|
br.Skip(3);
|
|
|
|
break;
|
|
|
|
case Constant.String:
|
|
|
|
case Constant.MethodType:
|
|
|
|
br.Skip(2);
|
|
|
|
break;
|
|
|
|
case Constant.Utf8:
|
|
|
|
isstub |= (utf8_cp[i] = br.ReadString("<unknown>")) == "IKVM.NET.Assembly";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ClassFormatError("Illegal constant pool type 0x{0:X}", tag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
br.ReadUInt16(); // access_flags
|
|
|
|
try
|
|
|
|
{
|
|
|
|
return String.Intern(utf8_cp[cpclass[br.ReadUInt16()]].Replace('/', '.'));
|
|
|
|
}
|
|
|
|
catch(Exception x)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0}: {1}", x.GetType().Name, x.Message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // STATIC_COMPILER
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal ClassFile(byte[] buf, int offset, int length, string inputClassName, ClassFileParseOptions options, object[] constantPoolPatches)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
BigEndianBinaryReader br = new BigEndianBinaryReader(buf, offset, length);
|
|
|
|
if(br.ReadUInt32() != 0xCAFEBABE)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Bad magic number)", inputClassName);
|
|
|
|
}
|
|
|
|
ushort minorVersion = br.ReadUInt16();
|
|
|
|
ushort majorVersion = br.ReadUInt16();
|
|
|
|
if((majorVersion & FLAG_MASK_MAJORVERSION) != majorVersion
|
|
|
|
|| majorVersion < SupportedVersions.Minimum
|
|
|
|
|| majorVersion > SupportedVersions.Maximum
|
|
|
|
|| (majorVersion == SupportedVersions.Minimum && minorVersion < 3)
|
|
|
|
|| (majorVersion == SupportedVersions.Maximum && minorVersion != 0))
|
|
|
|
{
|
|
|
|
throw new UnsupportedClassVersionError(inputClassName + " (" + majorVersion + "." + minorVersion + ")");
|
|
|
|
}
|
|
|
|
flags = majorVersion;
|
|
|
|
int constantpoolcount = br.ReadUInt16();
|
|
|
|
constantpool = new ConstantPoolItem[constantpoolcount];
|
|
|
|
string[] utf8_cp = new string[constantpoolcount];
|
|
|
|
for(int i = 1; i < constantpoolcount; i++)
|
|
|
|
{
|
|
|
|
Constant tag = (Constant)br.ReadByte();
|
|
|
|
switch(tag)
|
|
|
|
{
|
|
|
|
case Constant.Class:
|
|
|
|
constantpool[i] = new ConstantPoolItemClass(br);
|
|
|
|
break;
|
|
|
|
case Constant.Double:
|
|
|
|
constantpool[i] = new ConstantPoolItemDouble(br);
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
case Constant.Fieldref:
|
|
|
|
constantpool[i] = new ConstantPoolItemFieldref(br);
|
|
|
|
break;
|
|
|
|
case Constant.Float:
|
|
|
|
constantpool[i] = new ConstantPoolItemFloat(br);
|
|
|
|
break;
|
|
|
|
case Constant.Integer:
|
|
|
|
constantpool[i] = new ConstantPoolItemInteger(br);
|
|
|
|
break;
|
|
|
|
case Constant.InterfaceMethodref:
|
|
|
|
constantpool[i] = new ConstantPoolItemInterfaceMethodref(br);
|
|
|
|
break;
|
|
|
|
case Constant.Long:
|
|
|
|
constantpool[i] = new ConstantPoolItemLong(br);
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
case Constant.Methodref:
|
|
|
|
constantpool[i] = new ConstantPoolItemMethodref(br);
|
|
|
|
break;
|
|
|
|
case Constant.NameAndType:
|
|
|
|
constantpool[i] = new ConstantPoolItemNameAndType(br);
|
|
|
|
break;
|
|
|
|
case Constant.MethodHandle:
|
|
|
|
if (majorVersion < 51)
|
|
|
|
goto default;
|
|
|
|
constantpool[i] = new ConstantPoolItemMethodHandle(br);
|
|
|
|
break;
|
|
|
|
case Constant.MethodType:
|
|
|
|
if (majorVersion < 51)
|
|
|
|
goto default;
|
|
|
|
constantpool[i] = new ConstantPoolItemMethodType(br);
|
|
|
|
break;
|
|
|
|
case Constant.InvokeDynamic:
|
|
|
|
if (majorVersion < 51)
|
|
|
|
goto default;
|
|
|
|
constantpool[i] = new ConstantPoolItemInvokeDynamic(br);
|
|
|
|
break;
|
|
|
|
case Constant.String:
|
|
|
|
constantpool[i] = new ConstantPoolItemString(br);
|
|
|
|
break;
|
|
|
|
case Constant.Utf8:
|
|
|
|
utf8_cp[i] = br.ReadString(inputClassName);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ClassFormatError("{0} (Illegal constant pool type 0x{1:X})", inputClassName, tag);
|
|
|
|
}
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
if (constantPoolPatches != null)
|
|
|
|
{
|
|
|
|
PatchConstantPool(constantPoolPatches, utf8_cp, inputClassName);
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
for(int i = 1; i < constantpoolcount; i++)
|
|
|
|
{
|
|
|
|
if(constantpool[i] != null)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
constantpool[i].Resolve(this, utf8_cp, options);
|
|
|
|
}
|
|
|
|
catch(ClassFormatError x)
|
|
|
|
{
|
|
|
|
// HACK at this point we don't yet have the class name, so any exceptions throw
|
|
|
|
// are missing the class name
|
|
|
|
throw new ClassFormatError("{0} ({1})", inputClassName, x.Message);
|
|
|
|
}
|
|
|
|
catch(IndexOutOfRangeException)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i);
|
|
|
|
}
|
|
|
|
catch(InvalidCastException)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
access_flags = (Modifiers)br.ReadUInt16();
|
|
|
|
// NOTE although the vmspec says (in 4.1) that interfaces must be marked abstract, earlier versions of
|
|
|
|
// javac (JDK 1.1) didn't do this, so the VM doesn't enforce this rule for older class files.
|
|
|
|
// NOTE although the vmspec implies (in 4.1) that ACC_SUPER is illegal on interfaces, it doesn't enforce this
|
|
|
|
// for older class files.
|
|
|
|
// (See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6320322)
|
|
|
|
if((IsInterface && IsFinal)
|
|
|
|
|| (IsAbstract && IsFinal)
|
|
|
|
|| (majorVersion >= 49 && IsAnnotation && !IsInterface)
|
|
|
|
|| (majorVersion >= 49 && IsInterface && (!IsAbstract || IsSuper || IsEnum)))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Illegal class modifiers 0x{1:X})", inputClassName, access_flags);
|
|
|
|
}
|
|
|
|
this_class = br.ReadUInt16();
|
|
|
|
ValidateConstantPoolItemClass(inputClassName, this_class);
|
|
|
|
super_class = br.ReadUInt16();
|
|
|
|
ValidateConstantPoolItemClass(inputClassName, super_class);
|
2014-10-04 11:27:48 +01:00
|
|
|
if(IsInterface && (super_class == 0 || this.SuperClass.Name != "java.lang.Object"))
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Interfaces must have java.lang.Object as superclass)", Name);
|
|
|
|
}
|
|
|
|
// most checks are already done by ConstantPoolItemClass.Resolve, but since it allows
|
|
|
|
// array types, we do need to check for that
|
|
|
|
if(this.Name[0] == '[')
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Bad name");
|
|
|
|
}
|
|
|
|
int interfaces_count = br.ReadUInt16();
|
|
|
|
interfaces = new ConstantPoolItemClass[interfaces_count];
|
|
|
|
for(int i = 0; i < interfaces.Length; i++)
|
|
|
|
{
|
|
|
|
int index = br.ReadUInt16();
|
|
|
|
if(index == 0 || index >= constantpool.Length)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Illegal constant pool index)", Name);
|
|
|
|
}
|
|
|
|
ConstantPoolItemClass cpi = constantpool[index] as ConstantPoolItemClass;
|
|
|
|
if(cpi == null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Interface name has bad constant type)", Name);
|
|
|
|
}
|
|
|
|
interfaces[i] = cpi;
|
|
|
|
}
|
|
|
|
CheckDuplicates(interfaces, "Repetitive interface name");
|
|
|
|
int fields_count = br.ReadUInt16();
|
|
|
|
fields = new Field[fields_count];
|
|
|
|
for(int i = 0; i < fields_count; i++)
|
|
|
|
{
|
|
|
|
fields[i] = new Field(this, utf8_cp, br);
|
|
|
|
string name = fields[i].Name;
|
|
|
|
if(!IsValidFieldName(name, majorVersion))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Illegal field name \"{1}\")", Name, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CheckDuplicates<FieldOrMethod>(fields, "Repetitive field name/signature");
|
|
|
|
int methods_count = br.ReadUInt16();
|
|
|
|
methods = new Method[methods_count];
|
|
|
|
for(int i = 0; i < methods_count; i++)
|
|
|
|
{
|
|
|
|
methods[i] = new Method(this, utf8_cp, options, br);
|
|
|
|
string name = methods[i].Name;
|
|
|
|
string sig = methods[i].Signature;
|
|
|
|
if(!IsValidMethodName(name, majorVersion))
|
|
|
|
{
|
|
|
|
if(!ReferenceEquals(name, StringConstants.INIT) && !ReferenceEquals(name, StringConstants.CLINIT))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Illegal method name \"{1}\")", Name, name);
|
|
|
|
}
|
|
|
|
if(!sig.EndsWith("V"))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Method \"{1}\" has illegal signature \"{2}\")", Name, name, sig);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CheckDuplicates<FieldOrMethod>(methods, "Repetitive method name/signature");
|
|
|
|
int attributes_count = br.ReadUInt16();
|
|
|
|
for(int i = 0; i < attributes_count; i++)
|
|
|
|
{
|
|
|
|
switch(GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()))
|
|
|
|
{
|
|
|
|
case "Deprecated":
|
|
|
|
if(br.ReadUInt32() != 0)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid Deprecated attribute length");
|
|
|
|
}
|
|
|
|
flags |= FLAG_MASK_DEPRECATED;
|
|
|
|
break;
|
|
|
|
case "SourceFile":
|
|
|
|
if(br.ReadUInt32() != 2)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("SourceFile attribute has incorrect length");
|
|
|
|
}
|
|
|
|
sourceFile = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16());
|
|
|
|
break;
|
|
|
|
case "InnerClasses":
|
|
|
|
{
|
|
|
|
BigEndianBinaryReader rdr = br;
|
|
|
|
uint attribute_length = br.ReadUInt32();
|
|
|
|
ushort count = rdr.ReadUInt16();
|
|
|
|
if(this.MajorVersion >= 49 && attribute_length != 2 + count * (2 + 2 + 2 + 2))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (InnerClasses attribute has incorrect length)", this.Name);
|
|
|
|
}
|
|
|
|
innerClasses = new InnerClass[count];
|
|
|
|
for(int j = 0; j < innerClasses.Length; j++)
|
|
|
|
{
|
|
|
|
innerClasses[j].innerClass = rdr.ReadUInt16();
|
|
|
|
innerClasses[j].outerClass = rdr.ReadUInt16();
|
|
|
|
innerClasses[j].name = rdr.ReadUInt16();
|
|
|
|
innerClasses[j].accessFlags = (Modifiers)rdr.ReadUInt16();
|
|
|
|
if(innerClasses[j].innerClass != 0 && !(GetConstantPoolItem(innerClasses[j].innerClass) is ConstantPoolItemClass))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (inner_class_info_index has bad constant pool index)", this.Name);
|
|
|
|
}
|
|
|
|
if(innerClasses[j].outerClass != 0 && !(GetConstantPoolItem(innerClasses[j].outerClass) is ConstantPoolItemClass))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (outer_class_info_index has bad constant pool index)", this.Name);
|
|
|
|
}
|
|
|
|
if(innerClasses[j].name != 0 && utf8_cp[innerClasses[j].name] == null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (inner class name has bad constant pool index)", this.Name);
|
|
|
|
}
|
|
|
|
if(innerClasses[j].innerClass == innerClasses[j].outerClass)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Class is both inner and outer class)", this.Name);
|
|
|
|
}
|
|
|
|
if(innerClasses[j].innerClass != 0 && innerClasses[j].outerClass != 0)
|
|
|
|
{
|
|
|
|
MarkLinkRequiredConstantPoolItem(innerClasses[j].innerClass);
|
|
|
|
MarkLinkRequiredConstantPoolItem(innerClasses[j].outerClass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "Signature":
|
|
|
|
if(majorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
if(br.ReadUInt32() != 2)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Signature attribute has incorrect length");
|
|
|
|
}
|
|
|
|
signature = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16());
|
|
|
|
break;
|
|
|
|
case "EnclosingMethod":
|
|
|
|
if(majorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
if(br.ReadUInt32() != 4)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("EnclosingMethod attribute has incorrect length");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ushort class_index = br.ReadUInt16();
|
|
|
|
ushort method_index = br.ReadUInt16();
|
|
|
|
ValidateConstantPoolItemClass(inputClassName, class_index);
|
|
|
|
if(method_index == 0)
|
|
|
|
{
|
|
|
|
enclosingMethod = new string[] {
|
|
|
|
GetConstantPoolClass(class_index),
|
|
|
|
null,
|
|
|
|
null
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ConstantPoolItemNameAndType m = GetConstantPoolItem(method_index) as ConstantPoolItemNameAndType;
|
|
|
|
if(m == null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Bad constant pool index #{1})", inputClassName, method_index);
|
|
|
|
}
|
|
|
|
enclosingMethod = new string[] {
|
|
|
|
GetConstantPoolClass(class_index),
|
|
|
|
GetConstantPoolUtf8String(utf8_cp, m.name_index),
|
|
|
|
GetConstantPoolUtf8String(utf8_cp, m.descriptor_index).Replace('/', '.')
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "RuntimeVisibleAnnotations":
|
|
|
|
if(majorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
annotations = ReadAnnotations(br, this, utf8_cp);
|
|
|
|
break;
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
case "RuntimeInvisibleAnnotations":
|
|
|
|
if(majorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
foreach(object[] annot in ReadAnnotations(br, this, utf8_cp))
|
|
|
|
{
|
|
|
|
if(annot[1].Equals("Likvm/lang/Internal;"))
|
|
|
|
{
|
|
|
|
this.access_flags &= ~Modifiers.AccessMask;
|
|
|
|
flags |= FLAG_MASK_INTERNAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case "BootstrapMethods":
|
|
|
|
if(majorVersion < 51)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
bootstrapMethods = ReadBootstrapMethods(br, this);
|
|
|
|
break;
|
2014-10-04 11:27:48 +01:00
|
|
|
case "RuntimeVisibleTypeAnnotations":
|
|
|
|
if(majorVersion < 52)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
CreateUtf8ConstantPoolItems(utf8_cp);
|
|
|
|
runtimeVisibleTypeAnnotations = br.Section(br.ReadUInt32()).ToArray();
|
|
|
|
break;
|
2014-08-13 10:39:27 +01:00
|
|
|
case "IKVM.NET.Assembly":
|
|
|
|
if(br.ReadUInt32() != 2)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("IKVM.NET.Assembly attribute has incorrect length");
|
|
|
|
}
|
|
|
|
ikvmAssembly = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
br.Skip(br.ReadUInt32());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// validate the invokedynamic entries to point into the bootstrapMethods array
|
|
|
|
for(int i = 1; i < constantpoolcount; i++)
|
|
|
|
{
|
|
|
|
ConstantPoolItemInvokeDynamic cpi;
|
|
|
|
if(constantpool[i] != null
|
|
|
|
&& (cpi = constantpool[i] as ConstantPoolItemInvokeDynamic) != null)
|
|
|
|
{
|
|
|
|
if(bootstrapMethods == null || cpi.BootstrapMethod >= bootstrapMethods.Length)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Short length on BootstrapMethods in class file");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(br.Position != offset + length)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Extra bytes at the end of the class file");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(OverflowException)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Truncated class file (or section)");
|
|
|
|
}
|
|
|
|
catch(IndexOutOfRangeException)
|
|
|
|
{
|
|
|
|
// TODO we should throw more specific errors
|
|
|
|
throw new ClassFormatError("Unspecified class file format error");
|
|
|
|
}
|
|
|
|
// catch(Exception x)
|
|
|
|
// {
|
|
|
|
// Console.WriteLine(x);
|
|
|
|
// FileStream fs = File.Create(inputClassName + ".broken");
|
|
|
|
// fs.Write(buf, offset, length);
|
|
|
|
// fs.Close();
|
|
|
|
// throw;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
private void CreateUtf8ConstantPoolItems(string[] utf8_cp)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < constantpool.Length; i++)
|
|
|
|
{
|
|
|
|
if (constantpool[i] == null && utf8_cp[i] != null)
|
|
|
|
{
|
|
|
|
constantpool[i] = new ConstantPoolItemUtf8(utf8_cp[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
private void CheckDuplicates<T>(T[] members, string msg)
|
|
|
|
where T : IEquatable<T>
|
|
|
|
{
|
|
|
|
if (members.Length < 100)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < members.Length; i++)
|
|
|
|
{
|
|
|
|
for (int j = 0; j < i; j++)
|
|
|
|
{
|
|
|
|
if (members[i].Equals(members[j]))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} ({1})", Name, msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Dictionary<T, object> dict = new Dictionary<T, object>();
|
|
|
|
for (int i = 0; i < members.Length; i++)
|
|
|
|
{
|
|
|
|
if (dict.ContainsKey(members[i]))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} ({1})", Name, msg);
|
|
|
|
}
|
|
|
|
dict.Add(members[i], null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
private void PatchConstantPool(object[] constantPoolPatches, string[] utf8_cp, string inputClassName)
|
|
|
|
{
|
|
|
|
#if !STATIC_COMPILER && !FIRST_PASS
|
|
|
|
for (int i = 0; i < constantPoolPatches.Length; i++)
|
|
|
|
{
|
|
|
|
if (constantPoolPatches[i] != null)
|
|
|
|
{
|
|
|
|
if (utf8_cp[i] != null)
|
|
|
|
{
|
|
|
|
if (!(constantPoolPatches[i] is string))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Illegal utf8 patch at {0} in class file {1}", i, inputClassName);
|
|
|
|
}
|
|
|
|
utf8_cp[i] = (string)constantPoolPatches[i];
|
|
|
|
}
|
|
|
|
else if (constantpool[i] != null)
|
|
|
|
{
|
|
|
|
switch (constantpool[i].GetConstantType())
|
|
|
|
{
|
|
|
|
case ConstantType.String:
|
|
|
|
constantpool[i] = new ConstantPoolItemLiveObject(constantPoolPatches[i]);
|
|
|
|
break;
|
|
|
|
case ConstantType.Class:
|
|
|
|
java.lang.Class clazz;
|
|
|
|
string name;
|
|
|
|
if ((clazz = constantPoolPatches[i] as java.lang.Class) != null)
|
|
|
|
{
|
|
|
|
TypeWrapper tw = TypeWrapper.FromClass(clazz);
|
|
|
|
constantpool[i] = new ConstantPoolItemClass(tw.Name, tw);
|
|
|
|
}
|
|
|
|
else if ((name = constantPoolPatches[i] as string) != null)
|
|
|
|
{
|
|
|
|
constantpool[i] = new ConstantPoolItemClass(String.Intern(name.Replace('/', '.')), null);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Illegal class patch at {0} in class file {1}", i, inputClassName);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ConstantType.Integer:
|
|
|
|
((ConstantPoolItemInteger)constantpool[i]).v = ((java.lang.Integer)constantPoolPatches[i]).intValue();
|
|
|
|
break;
|
|
|
|
case ConstantType.Long:
|
|
|
|
((ConstantPoolItemLong)constantpool[i]).l = ((java.lang.Long)constantPoolPatches[i]).longValue();
|
|
|
|
break;
|
|
|
|
case ConstantType.Float:
|
|
|
|
((ConstantPoolItemFloat)constantpool[i]).v = ((java.lang.Float)constantPoolPatches[i]).floatValue();
|
|
|
|
break;
|
|
|
|
case ConstantType.Double:
|
|
|
|
((ConstantPoolItemDouble)constantpool[i]).d = ((java.lang.Double)constantPoolPatches[i]).doubleValue();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new NotImplementedException("ConstantPoolPatch: " + constantPoolPatches[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
private void MarkLinkRequiredConstantPoolItem(int index)
|
|
|
|
{
|
|
|
|
if (index > 0 && index < constantpool.Length && constantpool[index] != null)
|
|
|
|
{
|
|
|
|
constantpool[index].MarkLinkRequired();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static BootstrapMethod[] ReadBootstrapMethods(BigEndianBinaryReader br, ClassFile classFile)
|
|
|
|
{
|
|
|
|
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
|
|
|
|
ushort count = rdr.ReadUInt16();
|
|
|
|
BootstrapMethod[] bsm = new BootstrapMethod[count];
|
|
|
|
for(int i = 0; i < bsm.Length; i++)
|
|
|
|
{
|
|
|
|
ushort bsm_index = rdr.ReadUInt16();
|
|
|
|
if(bsm_index >= classFile.constantpool.Length || !(classFile.constantpool[bsm_index] is ConstantPoolItemMethodHandle))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("bootstrap_method_index {0} has bad constant type in class file {1}", bsm_index, classFile.Name);
|
|
|
|
}
|
|
|
|
classFile.MarkLinkRequiredConstantPoolItem(bsm_index);
|
|
|
|
ushort argument_count = rdr.ReadUInt16();
|
|
|
|
ushort[] args = new ushort[argument_count];
|
|
|
|
for(int j = 0; j < args.Length; j++)
|
|
|
|
{
|
|
|
|
ushort argument_index = rdr.ReadUInt16();
|
|
|
|
if(!classFile.IsValidConstant(argument_index))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("argument_index {0} has bad constant type in class file {1}", argument_index, classFile.Name);
|
|
|
|
}
|
|
|
|
classFile.MarkLinkRequiredConstantPoolItem(argument_index);
|
|
|
|
args[j] = argument_index;
|
|
|
|
}
|
|
|
|
bsm[i] = new BootstrapMethod(bsm_index, args);
|
|
|
|
}
|
|
|
|
if(!rdr.IsAtEnd)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Bad length on BootstrapMethods in class file {0}", classFile.Name);
|
|
|
|
}
|
|
|
|
return bsm;
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool IsValidConstant(ushort index)
|
|
|
|
{
|
|
|
|
if(index < constantpool.Length && constantpool[index] != null)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
constantpool[index].GetConstantType();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (InvalidOperationException) { }
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static object[] ReadAnnotations(BigEndianBinaryReader br, ClassFile classFile, string[] utf8_cp)
|
|
|
|
{
|
|
|
|
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
|
|
|
|
ushort num_annotations = rdr.ReadUInt16();
|
|
|
|
object[] annotations = new object[num_annotations];
|
|
|
|
for(int i = 0; i < annotations.Length; i++)
|
|
|
|
{
|
|
|
|
annotations[i] = ReadAnnotation(rdr, classFile, utf8_cp);
|
|
|
|
}
|
|
|
|
if(!rdr.IsAtEnd)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (RuntimeVisibleAnnotations attribute has wrong length)", classFile.Name);
|
|
|
|
}
|
|
|
|
return annotations;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static object ReadAnnotation(BigEndianBinaryReader rdr, ClassFile classFile, string[] utf8_cp)
|
|
|
|
{
|
|
|
|
string type = classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16());
|
|
|
|
ushort num_element_value_pairs = rdr.ReadUInt16();
|
|
|
|
object[] annot = new object[2 + num_element_value_pairs * 2];
|
|
|
|
annot[0] = AnnotationDefaultAttribute.TAG_ANNOTATION;
|
|
|
|
annot[1] = type;
|
|
|
|
for(int i = 0; i < num_element_value_pairs; i++)
|
|
|
|
{
|
|
|
|
annot[2 + i * 2 + 0] = classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16());
|
|
|
|
annot[2 + i * 2 + 1] = ReadAnnotationElementValue(rdr, classFile, utf8_cp);
|
|
|
|
}
|
|
|
|
return annot;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static object ReadAnnotationElementValue(BigEndianBinaryReader rdr, ClassFile classFile, string[] utf8_cp)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
byte tag = rdr.ReadByte();
|
|
|
|
switch (tag)
|
|
|
|
{
|
|
|
|
case (byte)'Z':
|
|
|
|
return classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16()) != 0;
|
|
|
|
case (byte)'B':
|
|
|
|
return (byte)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16());
|
|
|
|
case (byte)'C':
|
|
|
|
return (char)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16());
|
|
|
|
case (byte)'S':
|
|
|
|
return (short)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16());
|
|
|
|
case (byte)'I':
|
|
|
|
return classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16());
|
|
|
|
case (byte)'F':
|
|
|
|
return classFile.GetConstantPoolConstantFloat(rdr.ReadUInt16());
|
|
|
|
case (byte)'J':
|
|
|
|
return classFile.GetConstantPoolConstantLong(rdr.ReadUInt16());
|
|
|
|
case (byte)'D':
|
|
|
|
return classFile.GetConstantPoolConstantDouble(rdr.ReadUInt16());
|
|
|
|
case (byte)'s':
|
|
|
|
return classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16());
|
|
|
|
case (byte)'e':
|
|
|
|
{
|
|
|
|
ushort type_name_index = rdr.ReadUInt16();
|
|
|
|
ushort const_name_index = rdr.ReadUInt16();
|
|
|
|
return new object[] {
|
|
|
|
AnnotationDefaultAttribute.TAG_ENUM,
|
|
|
|
classFile.GetConstantPoolUtf8String(utf8_cp, type_name_index),
|
|
|
|
classFile.GetConstantPoolUtf8String(utf8_cp, const_name_index)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
case (byte)'c':
|
|
|
|
return new object[] {
|
|
|
|
AnnotationDefaultAttribute.TAG_CLASS,
|
|
|
|
classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16())
|
|
|
|
};
|
|
|
|
case (byte)'@':
|
|
|
|
return ReadAnnotation(rdr, classFile, utf8_cp);
|
|
|
|
case (byte)'[':
|
|
|
|
{
|
|
|
|
ushort num_values = rdr.ReadUInt16();
|
|
|
|
object[] array = new object[num_values + 1];
|
|
|
|
array[0] = AnnotationDefaultAttribute.TAG_ARRAY;
|
|
|
|
for (int i = 0; i < num_values; i++)
|
|
|
|
{
|
|
|
|
array[i + 1] = ReadAnnotationElementValue(rdr, classFile, utf8_cp);
|
|
|
|
}
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new ClassFormatError("Invalid tag {0} in annotation element_value", tag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (NullReferenceException)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
catch (InvalidCastException)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
catch (IndexOutOfRangeException)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
return new object[] { AnnotationDefaultAttribute.TAG_ERROR, "java.lang.IllegalArgumentException", "Wrong type at constant pool index" };
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ValidateConstantPoolItemClass(string classFile, ushort index)
|
|
|
|
{
|
|
|
|
if(index >= constantpool.Length || !(constantpool[index] is ConstantPoolItemClass))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Bad constant pool index #{1})", classFile, index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool IsValidMethodName(string name, int majorVersion)
|
|
|
|
{
|
|
|
|
if(name.Length == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for(int i = 0; i < name.Length; i++)
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
if(".;[/<>".IndexOf(name[i]) != -1)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return majorVersion >= 49 || IsValidPre49Identifier(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool IsValidFieldName(string name, int majorVersion)
|
|
|
|
{
|
|
|
|
if(name.Length == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for(int i = 0; i < name.Length; i++)
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
if(".;[/".IndexOf(name[i]) != -1)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return majorVersion >= 49 || IsValidPre49Identifier(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool IsValidPre49Identifier(string name)
|
|
|
|
{
|
|
|
|
if(!Char.IsLetter(name[0]) && "$_".IndexOf(name[0]) == -1)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for(int i = 1; i < name.Length; i++)
|
|
|
|
{
|
|
|
|
if(!Char.IsLetterOrDigit(name[i]) && "$_".IndexOf(name[i]) == -1)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static bool IsValidFieldSig(string sig)
|
|
|
|
{
|
|
|
|
return IsValidFieldSigImpl(sig, 0, sig.Length);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool IsValidFieldSigImpl(string sig, int start, int end)
|
|
|
|
{
|
|
|
|
if(start >= end)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
switch(sig[start])
|
|
|
|
{
|
|
|
|
case 'L':
|
|
|
|
return sig.IndexOf(';', start + 1) == end - 1;
|
|
|
|
case '[':
|
|
|
|
while(sig[start] == '[')
|
|
|
|
{
|
|
|
|
start++;
|
|
|
|
if(start == end)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return IsValidFieldSigImpl(sig, start, end);
|
|
|
|
case 'B':
|
|
|
|
case 'Z':
|
|
|
|
case 'C':
|
|
|
|
case 'S':
|
|
|
|
case 'I':
|
|
|
|
case 'J':
|
|
|
|
case 'F':
|
|
|
|
case 'D':
|
|
|
|
return start == end - 1;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static bool IsValidMethodSig(string sig)
|
|
|
|
{
|
|
|
|
if(sig.Length < 3 || sig[0] != '(')
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
int end = sig.IndexOf(')');
|
|
|
|
if(end == -1)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(!sig.EndsWith(")V") && !IsValidFieldSigImpl(sig, end + 1, sig.Length))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for(int i = 1; i < end; i++)
|
|
|
|
{
|
|
|
|
switch(sig[i])
|
|
|
|
{
|
|
|
|
case 'B':
|
|
|
|
case 'Z':
|
|
|
|
case 'C':
|
|
|
|
case 'S':
|
|
|
|
case 'I':
|
|
|
|
case 'J':
|
|
|
|
case 'F':
|
|
|
|
case 'D':
|
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
i = sig.IndexOf(';', i);
|
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
while(sig[i] == '[')
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if("BZCSIJFDL".IndexOf(sig[i]) == -1)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(sig[i] == 'L')
|
|
|
|
{
|
|
|
|
i = sig.IndexOf(';', i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(i == -1 || i >= end)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int MajorVersion
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return flags & FLAG_MASK_MAJORVERSION;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void Link(TypeWrapper thisType)
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
// this is not just an optimization, it's required for anonymous classes to be able to refer to themselves
|
|
|
|
((ConstantPoolItemClass)constantpool[this_class]).LinkSelf(thisType);
|
2014-08-13 10:39:27 +01:00
|
|
|
for(int i = 1; i < constantpool.Length; i++)
|
|
|
|
{
|
|
|
|
if(constantpool[i] != null)
|
|
|
|
{
|
|
|
|
constantpool[i].Link(thisType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Modifiers Modifiers
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return access_flags;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsAbstract
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
// interfaces are implicitly abstract
|
|
|
|
return (access_flags & (Modifiers.Abstract | Modifiers.Interface)) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsFinal
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Final) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsPublic
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Public) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsInterface
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Interface) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsEnum
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Enum) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsAnnotation
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Annotation) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsSuper
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Super) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void RemoveUnusedFields()
|
|
|
|
{
|
|
|
|
List<Field> list = new List<Field>();
|
|
|
|
foreach(Field f in fields)
|
|
|
|
{
|
|
|
|
if(f.IsPrivate && f.IsStatic && f.Name != "serialVersionUID" && !IsReferenced(f))
|
|
|
|
{
|
|
|
|
// unused, so we skip it
|
|
|
|
Tracer.Info(Tracer.Compiler, "Unused field {0}::{1}", this.Name, f.Name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
list.Add(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fields = list.ToArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool IsReferenced(Field fld)
|
|
|
|
{
|
|
|
|
foreach(ConstantPoolItem cpi in constantpool)
|
|
|
|
{
|
|
|
|
ConstantPoolItemFieldref fieldref = cpi as ConstantPoolItemFieldref;
|
|
|
|
if(fieldref != null &&
|
|
|
|
fieldref.Class == this.Name &&
|
|
|
|
fieldref.Name == fld.Name &&
|
|
|
|
fieldref.Signature == fld.Signature)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ConstantPoolItemFieldref GetFieldref(int index)
|
|
|
|
{
|
|
|
|
return (ConstantPoolItemFieldref)constantpool[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
// this won't throw an exception if index is invalid
|
|
|
|
// (used by IsSideEffectFreeStaticInitializer)
|
|
|
|
internal ConstantPoolItemFieldref SafeGetFieldref(int index)
|
|
|
|
{
|
|
|
|
if(index > 0 && index < constantpool.Length)
|
|
|
|
{
|
|
|
|
return constantpool[index] as ConstantPoolItemFieldref;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE this returns an MI, because it used for both normal methods and interface methods
|
|
|
|
internal ConstantPoolItemMI GetMethodref(int index)
|
|
|
|
{
|
|
|
|
return (ConstantPoolItemMI)constantpool[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
// this won't throw an exception if index is invalid
|
|
|
|
// (used by IsAccessBridge)
|
|
|
|
internal ConstantPoolItemMI SafeGetMethodref(int index)
|
|
|
|
{
|
|
|
|
if (index > 0 && index < constantpool.Length)
|
|
|
|
{
|
|
|
|
return constantpool[index] as ConstantPoolItemMI;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ConstantPoolItemInvokeDynamic GetInvokeDynamic(int index)
|
|
|
|
{
|
|
|
|
return (ConstantPoolItemInvokeDynamic)constantpool[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
private ConstantPoolItem GetConstantPoolItem(int index)
|
|
|
|
{
|
|
|
|
return constantpool[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string GetConstantPoolClass(int index)
|
|
|
|
{
|
|
|
|
return ((ConstantPoolItemClass)constantpool[index]).Name;
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool SafeIsConstantPoolClass(int index)
|
|
|
|
{
|
|
|
|
if(index > 0 && index < constantpool.Length)
|
|
|
|
{
|
|
|
|
return constantpool[index] as ConstantPoolItemClass != null;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper GetConstantPoolClassType(int index)
|
|
|
|
{
|
|
|
|
return ((ConstantPoolItemClass)constantpool[index]).GetClassType();
|
|
|
|
}
|
|
|
|
|
|
|
|
private string GetConstantPoolUtf8String(string[] utf8_cp, int index)
|
|
|
|
{
|
|
|
|
string s = utf8_cp[index];
|
|
|
|
if(s == null)
|
|
|
|
{
|
|
|
|
if(this_class == 0)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Bad constant pool index #{0}", index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Bad constant pool index #{1})", this.Name, index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ConstantType GetConstantPoolConstantType(int index)
|
|
|
|
{
|
|
|
|
return constantpool[index].GetConstantType();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal double GetConstantPoolConstantDouble(int index)
|
|
|
|
{
|
|
|
|
return ((ConstantPoolItemDouble)constantpool[index]).Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal float GetConstantPoolConstantFloat(int index)
|
|
|
|
{
|
|
|
|
return ((ConstantPoolItemFloat)constantpool[index]).Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int GetConstantPoolConstantInteger(int index)
|
|
|
|
{
|
|
|
|
return ((ConstantPoolItemInteger)constantpool[index]).Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal long GetConstantPoolConstantLong(int index)
|
|
|
|
{
|
|
|
|
return ((ConstantPoolItemLong)constantpool[index]).Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string GetConstantPoolConstantString(int index)
|
|
|
|
{
|
|
|
|
return ((ConstantPoolItemString)constantpool[index]).Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ConstantPoolItemMethodHandle GetConstantPoolConstantMethodHandle(int index)
|
|
|
|
{
|
|
|
|
return (ConstantPoolItemMethodHandle)constantpool[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ConstantPoolItemMethodType GetConstantPoolConstantMethodType(int index)
|
|
|
|
{
|
|
|
|
return (ConstantPoolItemMethodType)constantpool[index];
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal object GetConstantPoolConstantLiveObject(int index)
|
|
|
|
{
|
|
|
|
return ((ConstantPoolItemLiveObject)constantpool[index]).Value;
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
internal string Name
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return GetConstantPoolClass(this_class);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal ConstantPoolItemClass SuperClass
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
return (ConstantPoolItemClass)constantpool[super_class];
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Field[] Fields
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return fields;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Method[] Methods
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return methods;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ConstantPoolItemClass[] Interfaces
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return interfaces;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string SourceFileAttribute
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return sourceFile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string SourcePath
|
|
|
|
{
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
get { return sourcePath; }
|
|
|
|
set { sourcePath = value; }
|
|
|
|
#else
|
|
|
|
get { return sourceFile; }
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
internal object[] Annotations
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return annotations;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string GenericSignature
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return signature;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string[] EnclosingMethod
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return enclosingMethod;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal byte[] RuntimeVisibleTypeAnnotations
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return runtimeVisibleTypeAnnotations;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal object[] GetConstantPool()
|
|
|
|
{
|
|
|
|
object[] cp = new object[constantpool.Length];
|
|
|
|
for (int i = 1; i < cp.Length; i++)
|
|
|
|
{
|
|
|
|
if (constantpool[i] != null)
|
|
|
|
{
|
|
|
|
cp[i] = constantpool[i].GetRuntimeValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cp;
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
internal string IKVMAssemblyAttribute
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return ikvmAssembly;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool DeprecatedAttribute
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (flags & FLAG_MASK_DEPRECATED) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsInternal
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (flags & FLAG_MASK_INTERNAL) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// for use by ikvmc (to implement the -privatepackage option)
|
|
|
|
internal void SetInternal()
|
|
|
|
{
|
|
|
|
access_flags &= ~Modifiers.AccessMask;
|
|
|
|
flags |= FLAG_MASK_INTERNAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool HasInitializedFields
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
foreach (Field f in fields)
|
|
|
|
{
|
|
|
|
if (f.IsStatic && !f.IsFinal && f.ConstantValue != null)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal BootstrapMethod GetBootstrapMethod(int index)
|
|
|
|
{
|
|
|
|
return bootstrapMethods[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
internal struct BootstrapMethod
|
|
|
|
{
|
|
|
|
private ushort bsm_index;
|
|
|
|
private ushort[] args;
|
|
|
|
|
|
|
|
internal BootstrapMethod(ushort bsm_index, ushort[] args)
|
|
|
|
{
|
|
|
|
this.bsm_index = bsm_index;
|
|
|
|
this.args = args;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int BootstrapMethodIndex
|
|
|
|
{
|
|
|
|
get { return bsm_index; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int ArgumentCount
|
|
|
|
{
|
|
|
|
get { return args.Length; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int GetArgument(int index)
|
|
|
|
{
|
|
|
|
return args[index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal struct InnerClass
|
|
|
|
{
|
|
|
|
internal ushort innerClass; // ConstantPoolItemClass
|
|
|
|
internal ushort outerClass; // ConstantPoolItemClass
|
|
|
|
internal ushort name; // ConstantPoolItemUtf8
|
|
|
|
internal Modifiers accessFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal InnerClass[] InnerClasses
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return innerClasses;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal enum RefKind
|
|
|
|
{
|
|
|
|
getField = 1,
|
|
|
|
getStatic = 2,
|
|
|
|
putField = 3,
|
|
|
|
putStatic = 4,
|
|
|
|
invokeVirtual = 5,
|
|
|
|
invokeStatic = 6,
|
|
|
|
invokeSpecial = 7,
|
|
|
|
newInvokeSpecial = 8,
|
|
|
|
invokeInterface = 9
|
|
|
|
}
|
|
|
|
|
|
|
|
internal enum ConstantType
|
|
|
|
{
|
|
|
|
Integer,
|
|
|
|
Long,
|
|
|
|
Float,
|
|
|
|
Double,
|
|
|
|
String,
|
|
|
|
Class,
|
|
|
|
MethodHandle,
|
|
|
|
MethodType,
|
2014-10-04 11:27:48 +01:00
|
|
|
LiveObject, // used by anonymous class constant pool patching
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
internal abstract class ConstantPoolItem
|
|
|
|
{
|
|
|
|
internal virtual void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
internal virtual void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
internal virtual ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal virtual void MarkLinkRequired()
|
|
|
|
{
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
|
|
|
|
// this is used for sun.reflect.ConstantPool
|
|
|
|
// it returns a boxed System.Int32, System.Int64, System.Float, System.Double or a string
|
|
|
|
internal virtual object GetRuntimeValue()
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class ConstantPoolItemClass : ConstantPoolItem, IEquatable<ConstantPoolItemClass>
|
|
|
|
{
|
|
|
|
private ushort name_index;
|
|
|
|
private string name;
|
|
|
|
private TypeWrapper typeWrapper;
|
2014-10-04 11:27:48 +01:00
|
|
|
private static char[] invalidJava15Characters = { '.', ';', '[', ']' };
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
internal ConstantPoolItemClass(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
name_index = br.ReadUInt16();
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal ConstantPoolItemClass(string name, TypeWrapper typeWrapper)
|
|
|
|
{
|
|
|
|
this.name = name;
|
|
|
|
this.typeWrapper = typeWrapper;
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options)
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
// if the item was patched, we already have a name
|
|
|
|
if(name != null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
name = classFile.GetConstantPoolUtf8String(utf8_cp, name_index);
|
|
|
|
if(name.Length > 0)
|
|
|
|
{
|
|
|
|
// We don't enforce the strict class name rules in the static compiler, since HotSpot doesn't enforce *any* rules on
|
|
|
|
// class names for the system (and boot) class loader. We still need to enforce the 1.5 restrictions, because we
|
|
|
|
// rely on those invariants.
|
|
|
|
#if !STATIC_COMPILER
|
|
|
|
if(classFile.MajorVersion < 49 && (options & ClassFileParseOptions.RelaxedClassNameValidation) == 0)
|
|
|
|
{
|
|
|
|
char prev = name[0];
|
|
|
|
if(Char.IsLetter(prev) || prev == '$' || prev == '_' || prev == '[' || prev == '/')
|
|
|
|
{
|
|
|
|
int skip = 1;
|
|
|
|
int end = name.Length;
|
|
|
|
if(prev == '[')
|
|
|
|
{
|
|
|
|
if(!IsValidFieldSig(name))
|
|
|
|
{
|
|
|
|
goto barf;
|
|
|
|
}
|
|
|
|
while(name[skip] == '[')
|
|
|
|
{
|
|
|
|
skip++;
|
|
|
|
}
|
|
|
|
if(name.EndsWith(";"))
|
|
|
|
{
|
|
|
|
end--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(int i = skip; i < end; i++)
|
|
|
|
{
|
|
|
|
char c = name[i];
|
|
|
|
if(!Char.IsLetterOrDigit(c) && c != '$' && c != '_' && (c != '/' || prev == '/'))
|
|
|
|
{
|
|
|
|
goto barf;
|
|
|
|
}
|
|
|
|
prev = c;
|
|
|
|
}
|
|
|
|
name = String.Intern(name.Replace('/', '.'));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
// since 1.5 the restrictions on class names have been greatly reduced
|
2014-10-04 11:27:48 +01:00
|
|
|
int start = 0;
|
2014-08-13 10:39:27 +01:00
|
|
|
int end = name.Length;
|
|
|
|
if(name[0] == '[')
|
|
|
|
{
|
|
|
|
if(!IsValidFieldSig(name))
|
|
|
|
{
|
|
|
|
goto barf;
|
|
|
|
}
|
|
|
|
// the semicolon is only allowed at the end and IsValidFieldSig enforces this,
|
|
|
|
// but since invalidJava15Characters contains the semicolon, we decrement end
|
|
|
|
// to make the following check against invalidJava15Characters ignore the
|
|
|
|
// trailing semicolon.
|
|
|
|
if(name[end - 1] == ';')
|
|
|
|
{
|
|
|
|
end--;
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
while(name[start] == '[')
|
|
|
|
{
|
|
|
|
start++;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
if(name.IndexOfAny(invalidJava15Characters, start, end - start) >= 0)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
goto barf;
|
|
|
|
}
|
|
|
|
name = String.Intern(name.Replace('/', '.'));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
barf:
|
|
|
|
throw new ClassFormatError("Invalid class name \"{0}\"", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void MarkLinkRequired()
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
if(typeWrapper == null)
|
|
|
|
{
|
|
|
|
typeWrapper = VerifierTypeWrapper.Null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void LinkSelf(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
this.typeWrapper = thisType;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
if(typeWrapper == VerifierTypeWrapper.Null)
|
|
|
|
{
|
|
|
|
TypeWrapper tw = ClassLoaderWrapper.LoadClassNoThrow(thisType.GetClassLoader(), name, true);
|
|
|
|
#if !STATIC_COMPILER && !FIRST_PASS
|
|
|
|
if(!tw.IsUnloadable)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
thisType.GetClassLoader().CheckPackageAccess(tw, thisType.ClassObject.pd);
|
|
|
|
}
|
|
|
|
catch(java.lang.SecurityException)
|
|
|
|
{
|
|
|
|
tw = new UnloadableTypeWrapper(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
typeWrapper = tw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Name
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper GetClassType()
|
|
|
|
{
|
|
|
|
return typeWrapper;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.Class;
|
|
|
|
}
|
|
|
|
|
|
|
|
public sealed override int GetHashCode()
|
|
|
|
{
|
|
|
|
return name.GetHashCode();
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool Equals(ConstantPoolItemClass other)
|
|
|
|
{
|
|
|
|
return ReferenceEquals(name, other.name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private sealed class ConstantPoolItemDouble : ConstantPoolItem
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
internal double d;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
internal ConstantPoolItemDouble(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
d = br.ReadDouble();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.Double;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal double Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
|
|
|
|
internal override object GetRuntimeValue()
|
|
|
|
{
|
|
|
|
return d;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
internal abstract class ConstantPoolItemFMI : ConstantPoolItem
|
|
|
|
{
|
|
|
|
private ushort class_index;
|
|
|
|
private ushort name_and_type_index;
|
|
|
|
private ConstantPoolItemClass clazz;
|
|
|
|
private string name;
|
|
|
|
private string descriptor;
|
|
|
|
|
|
|
|
internal ConstantPoolItemFMI(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
class_index = br.ReadUInt16();
|
|
|
|
name_and_type_index = br.ReadUInt16();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options)
|
|
|
|
{
|
|
|
|
ConstantPoolItemNameAndType name_and_type = (ConstantPoolItemNameAndType)classFile.GetConstantPoolItem(name_and_type_index);
|
|
|
|
clazz = (ConstantPoolItemClass)classFile.GetConstantPoolItem(class_index);
|
|
|
|
// if the constant pool items referred to were strings, GetConstantPoolItem returns null
|
|
|
|
if(name_and_type == null || clazz == null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Bad index in constant pool");
|
|
|
|
}
|
|
|
|
name = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.name_index));
|
|
|
|
descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.descriptor_index);
|
|
|
|
Validate(name, descriptor, classFile.MajorVersion);
|
|
|
|
descriptor = String.Intern(descriptor.Replace('/', '.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected abstract void Validate(string name, string descriptor, int majorVersion);
|
|
|
|
|
|
|
|
internal override void MarkLinkRequired()
|
|
|
|
{
|
|
|
|
clazz.MarkLinkRequired();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
clazz.Link(thisType);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Name
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Signature
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return descriptor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Class
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return clazz.Name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper GetClassType()
|
|
|
|
{
|
|
|
|
return clazz.GetClassType();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal abstract MemberWrapper GetMember();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class ConstantPoolItemFieldref : ConstantPoolItemFMI
|
|
|
|
{
|
|
|
|
private FieldWrapper field;
|
|
|
|
private TypeWrapper fieldTypeWrapper;
|
|
|
|
|
|
|
|
internal ConstantPoolItemFieldref(BigEndianBinaryReader br) : base(br)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void Validate(string name, string descriptor, int majorVersion)
|
|
|
|
{
|
|
|
|
if(!IsValidFieldSig(descriptor))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid field signature \"{0}\"", descriptor);
|
|
|
|
}
|
|
|
|
if(!IsValidFieldName(name, majorVersion))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid field name \"{0}\"", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper GetFieldType()
|
|
|
|
{
|
|
|
|
return fieldTypeWrapper;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
base.Link(thisType);
|
|
|
|
lock(this)
|
|
|
|
{
|
|
|
|
if(fieldTypeWrapper != null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FieldWrapper fw = null;
|
|
|
|
TypeWrapper wrapper = GetClassType();
|
|
|
|
if(wrapper == null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(!wrapper.IsUnloadable)
|
|
|
|
{
|
|
|
|
fw = wrapper.GetFieldWrapper(Name, Signature);
|
|
|
|
if(fw != null)
|
|
|
|
{
|
|
|
|
fw.Link();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ClassLoaderWrapper classLoader = thisType.GetClassLoader();
|
|
|
|
TypeWrapper fld = classLoader.FieldTypeWrapperFromSigNoThrow(this.Signature);
|
|
|
|
lock(this)
|
|
|
|
{
|
|
|
|
if(fieldTypeWrapper == null)
|
|
|
|
{
|
|
|
|
fieldTypeWrapper = fld;
|
|
|
|
field = fw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal FieldWrapper GetField()
|
|
|
|
{
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override MemberWrapper GetMember()
|
|
|
|
{
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal class ConstantPoolItemMI : ConstantPoolItemFMI
|
|
|
|
{
|
|
|
|
private TypeWrapper[] argTypeWrappers;
|
|
|
|
private TypeWrapper retTypeWrapper;
|
|
|
|
protected MethodWrapper method;
|
|
|
|
protected MethodWrapper invokespecialMethod;
|
|
|
|
|
|
|
|
internal ConstantPoolItemMI(BigEndianBinaryReader br) : base(br)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void Validate(string name, string descriptor, int majorVersion)
|
|
|
|
{
|
|
|
|
if(!IsValidMethodSig(descriptor))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Method {0} has invalid signature {1}", name, descriptor);
|
|
|
|
}
|
|
|
|
if(!IsValidMethodName(name, majorVersion))
|
|
|
|
{
|
|
|
|
if(!ReferenceEquals(name, StringConstants.INIT))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid method name \"{0}\"", name);
|
|
|
|
}
|
|
|
|
if(!descriptor.EndsWith("V"))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Method {0} has invalid signature {1}", name, descriptor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
base.Link(thisType);
|
|
|
|
lock(this)
|
|
|
|
{
|
|
|
|
if(argTypeWrappers != null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ClassLoaderWrapper classLoader = thisType.GetClassLoader();
|
|
|
|
TypeWrapper[] args = classLoader.ArgTypeWrapperListFromSigNoThrow(this.Signature);
|
|
|
|
TypeWrapper ret = classLoader.RetTypeWrapperFromSigNoThrow(this.Signature);
|
|
|
|
lock(this)
|
|
|
|
{
|
|
|
|
if(argTypeWrappers == null)
|
|
|
|
{
|
|
|
|
argTypeWrappers = args;
|
|
|
|
retTypeWrapper = ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper[] GetArgTypes()
|
|
|
|
{
|
|
|
|
return argTypeWrappers;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper GetRetType()
|
|
|
|
{
|
|
|
|
return retTypeWrapper;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal MethodWrapper GetMethod()
|
|
|
|
{
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal MethodWrapper GetMethodForInvokespecial()
|
|
|
|
{
|
|
|
|
return invokespecialMethod != null ? invokespecialMethod : method;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override MemberWrapper GetMember()
|
|
|
|
{
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class ConstantPoolItemMethodref : ConstantPoolItemMI
|
|
|
|
{
|
|
|
|
internal ConstantPoolItemMethodref(BigEndianBinaryReader br) : base(br)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
base.Link(thisType);
|
|
|
|
TypeWrapper wrapper = GetClassType();
|
|
|
|
if(wrapper != null && !wrapper.IsUnloadable)
|
|
|
|
{
|
|
|
|
method = wrapper.GetMethodWrapper(Name, Signature, !ReferenceEquals(Name, StringConstants.INIT));
|
|
|
|
if(method != null)
|
|
|
|
{
|
|
|
|
method.Link();
|
|
|
|
}
|
|
|
|
if(Name != StringConstants.INIT
|
|
|
|
&& !thisType.IsInterface
|
|
|
|
&& (!JVM.AllowNonVirtualCalls || (thisType.Modifiers & Modifiers.Super) == Modifiers.Super)
|
|
|
|
&& thisType != wrapper
|
|
|
|
&& thisType.IsSubTypeOf(wrapper))
|
|
|
|
{
|
|
|
|
invokespecialMethod = thisType.BaseTypeWrapper.GetMethodWrapper(Name, Signature, true);
|
|
|
|
if(invokespecialMethod != null)
|
|
|
|
{
|
|
|
|
invokespecialMethod.Link();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class ConstantPoolItemInterfaceMethodref : ConstantPoolItemMI
|
|
|
|
{
|
|
|
|
internal ConstantPoolItemInterfaceMethodref(BigEndianBinaryReader br) : base(br)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
private static MethodWrapper GetInterfaceMethod(TypeWrapper wrapper, string name, string sig)
|
|
|
|
{
|
|
|
|
if(wrapper.IsUnloadable)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
MethodWrapper method = wrapper.GetMethodWrapper(name, sig, false);
|
|
|
|
if(method != null)
|
|
|
|
{
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
TypeWrapper[] interfaces = wrapper.Interfaces;
|
|
|
|
for(int i = 0; i < interfaces.Length; i++)
|
|
|
|
{
|
|
|
|
method = GetInterfaceMethod(interfaces[i], name, sig);
|
|
|
|
if(method != null)
|
|
|
|
{
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
base.Link(thisType);
|
|
|
|
TypeWrapper wrapper = GetClassType();
|
|
|
|
if(wrapper != null)
|
|
|
|
{
|
|
|
|
method = GetInterfaceMethod(wrapper, Name, Signature);
|
|
|
|
if(method == null)
|
|
|
|
{
|
|
|
|
// NOTE vmspec 5.4.3.4 clearly states that an interfacemethod may also refer to a method in Object
|
|
|
|
method = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(Name, Signature, false);
|
|
|
|
}
|
|
|
|
if(method != null)
|
|
|
|
{
|
|
|
|
method.Link();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private sealed class ConstantPoolItemFloat : ConstantPoolItem
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
internal float v;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
internal ConstantPoolItemFloat(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
v = br.ReadSingle();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.Float;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal float Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
|
|
|
|
internal override object GetRuntimeValue()
|
|
|
|
{
|
|
|
|
return v;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private sealed class ConstantPoolItemInteger : ConstantPoolItem
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
internal int v;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
internal ConstantPoolItemInteger(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
v = br.ReadInt32();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.Integer;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
|
|
|
|
internal override object GetRuntimeValue()
|
|
|
|
{
|
|
|
|
return v;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private sealed class ConstantPoolItemLong : ConstantPoolItem
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
internal long l;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
internal ConstantPoolItemLong(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
l = br.ReadInt64();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.Long;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal long Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
|
|
|
|
internal override object GetRuntimeValue()
|
|
|
|
{
|
|
|
|
return l;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private sealed class ConstantPoolItemNameAndType : ConstantPoolItem
|
|
|
|
{
|
|
|
|
internal ushort name_index;
|
|
|
|
internal ushort descriptor_index;
|
|
|
|
|
|
|
|
internal ConstantPoolItemNameAndType(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
name_index = br.ReadUInt16();
|
|
|
|
descriptor_index = br.ReadUInt16();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options)
|
|
|
|
{
|
|
|
|
if(classFile.GetConstantPoolUtf8String(utf8_cp, name_index) == null
|
|
|
|
|| classFile.GetConstantPoolUtf8String(utf8_cp, descriptor_index) == null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Illegal constant pool index");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class ConstantPoolItemMethodHandle : ConstantPoolItem
|
|
|
|
{
|
|
|
|
private byte ref_kind;
|
|
|
|
private ushort method_index;
|
|
|
|
private ConstantPoolItemFMI cpi;
|
|
|
|
|
|
|
|
internal ConstantPoolItemMethodHandle(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
ref_kind = br.ReadByte();
|
|
|
|
method_index = br.ReadUInt16();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options)
|
|
|
|
{
|
|
|
|
switch ((RefKind)ref_kind)
|
|
|
|
{
|
|
|
|
case RefKind.getField:
|
|
|
|
case RefKind.getStatic:
|
|
|
|
case RefKind.putField:
|
|
|
|
case RefKind.putStatic:
|
|
|
|
cpi = classFile.GetConstantPoolItem(method_index) as ConstantPoolItemFieldref;
|
|
|
|
break;
|
|
|
|
case RefKind.invokeSpecial:
|
|
|
|
case RefKind.invokeVirtual:
|
|
|
|
case RefKind.invokeStatic:
|
|
|
|
case RefKind.newInvokeSpecial:
|
|
|
|
cpi = classFile.GetConstantPoolItem(method_index) as ConstantPoolItemMethodref;
|
2014-10-04 11:27:48 +01:00
|
|
|
if (cpi == null && classFile.MajorVersion >= 52 && ((RefKind)ref_kind == RefKind.invokeStatic || (RefKind)ref_kind == RefKind.invokeSpecial))
|
2014-08-13 10:39:27 +01:00
|
|
|
goto case RefKind.invokeInterface;
|
|
|
|
break;
|
|
|
|
case RefKind.invokeInterface:
|
|
|
|
cpi = classFile.GetConstantPoolItem(method_index) as ConstantPoolItemInterfaceMethodref;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cpi == null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid constant pool item MethodHandle");
|
|
|
|
}
|
|
|
|
if (ReferenceEquals(cpi.Name, StringConstants.INIT) && Kind != RefKind.newInvokeSpecial)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Bad method name");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void MarkLinkRequired()
|
|
|
|
{
|
|
|
|
cpi.MarkLinkRequired();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Class
|
|
|
|
{
|
|
|
|
get { return cpi.Class; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Name
|
|
|
|
{
|
|
|
|
get { return cpi.Name; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Signature
|
|
|
|
{
|
|
|
|
get { return cpi.Signature; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ConstantPoolItemFMI MemberConstantPoolItem
|
|
|
|
{
|
|
|
|
get { return cpi; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal RefKind Kind
|
|
|
|
{
|
|
|
|
get { return (RefKind)ref_kind; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal MemberWrapper Member
|
|
|
|
{
|
|
|
|
get { return cpi.GetMember(); }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper GetClassType()
|
|
|
|
{
|
|
|
|
return cpi.GetClassType();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
cpi.Link(thisType);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.MethodHandle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class ConstantPoolItemMethodType : ConstantPoolItem
|
|
|
|
{
|
|
|
|
private ushort signature_index;
|
|
|
|
private string descriptor;
|
|
|
|
private TypeWrapper[] argTypeWrappers;
|
|
|
|
private TypeWrapper retTypeWrapper;
|
|
|
|
|
|
|
|
internal ConstantPoolItemMethodType(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
signature_index = br.ReadUInt16();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options)
|
|
|
|
{
|
|
|
|
string descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, signature_index);
|
|
|
|
if (descriptor == null || !IsValidMethodSig(descriptor))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid MethodType signature");
|
|
|
|
}
|
|
|
|
this.descriptor = String.Intern(descriptor.Replace('/', '.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
lock (this)
|
|
|
|
{
|
|
|
|
if (argTypeWrappers != null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ClassLoaderWrapper classLoader = thisType.GetClassLoader();
|
|
|
|
TypeWrapper[] args = classLoader.ArgTypeWrapperListFromSigNoThrow(descriptor);
|
|
|
|
TypeWrapper ret = classLoader.RetTypeWrapperFromSigNoThrow(descriptor);
|
|
|
|
lock (this)
|
|
|
|
{
|
|
|
|
if (argTypeWrappers == null)
|
|
|
|
{
|
|
|
|
argTypeWrappers = args;
|
|
|
|
retTypeWrapper = ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Signature
|
|
|
|
{
|
|
|
|
get { return descriptor; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper[] GetArgTypes()
|
|
|
|
{
|
|
|
|
return argTypeWrappers;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper GetRetType()
|
|
|
|
{
|
|
|
|
return retTypeWrapper;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.MethodType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class ConstantPoolItemInvokeDynamic : ConstantPoolItem
|
|
|
|
{
|
|
|
|
private ushort bootstrap_specifier_index;
|
|
|
|
private ushort name_and_type_index;
|
|
|
|
private string name;
|
|
|
|
private string descriptor;
|
|
|
|
private TypeWrapper[] argTypeWrappers;
|
|
|
|
private TypeWrapper retTypeWrapper;
|
|
|
|
|
|
|
|
internal ConstantPoolItemInvokeDynamic(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
bootstrap_specifier_index = br.ReadUInt16();
|
|
|
|
name_and_type_index = br.ReadUInt16();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options)
|
|
|
|
{
|
|
|
|
ConstantPoolItemNameAndType name_and_type = (ConstantPoolItemNameAndType)classFile.GetConstantPoolItem(name_and_type_index);
|
|
|
|
// if the constant pool items referred to were strings, GetConstantPoolItem returns null
|
|
|
|
if (name_and_type == null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Bad index in constant pool");
|
|
|
|
}
|
|
|
|
name = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.name_index));
|
|
|
|
descriptor = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.descriptor_index).Replace('/', '.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Link(TypeWrapper thisType)
|
|
|
|
{
|
|
|
|
lock (this)
|
|
|
|
{
|
|
|
|
if (argTypeWrappers != null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ClassLoaderWrapper classLoader = thisType.GetClassLoader();
|
|
|
|
TypeWrapper[] args = classLoader.ArgTypeWrapperListFromSigNoThrow(descriptor);
|
|
|
|
TypeWrapper ret = classLoader.RetTypeWrapperFromSigNoThrow(descriptor);
|
|
|
|
lock (this)
|
|
|
|
{
|
|
|
|
if (argTypeWrappers == null)
|
|
|
|
{
|
|
|
|
argTypeWrappers = args;
|
|
|
|
retTypeWrapper = ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper[] GetArgTypes()
|
|
|
|
{
|
|
|
|
return argTypeWrappers;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal TypeWrapper GetRetType()
|
|
|
|
{
|
|
|
|
return retTypeWrapper;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Name
|
|
|
|
{
|
|
|
|
get { return name; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ushort BootstrapMethod
|
|
|
|
{
|
|
|
|
get { return bootstrap_specifier_index; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private sealed class ConstantPoolItemString : ConstantPoolItem
|
|
|
|
{
|
|
|
|
private ushort string_index;
|
|
|
|
private string s;
|
|
|
|
|
|
|
|
internal ConstantPoolItemString(BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
string_index = br.ReadUInt16();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options)
|
|
|
|
{
|
|
|
|
s = classFile.GetConstantPoolUtf8String(utf8_cp, string_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.String;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
// this is only used to copy strings into "constantpool" when we see a RuntimeVisibleTypeAnnotations attribute,
|
|
|
|
// because we need a consistent way of exposing constant pool items to the runtime and that case
|
|
|
|
private sealed class ConstantPoolItemUtf8 : ConstantPoolItem
|
|
|
|
{
|
|
|
|
private readonly string str;
|
|
|
|
|
|
|
|
internal ConstantPoolItemUtf8(string str)
|
|
|
|
{
|
|
|
|
this.str = str;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override object GetRuntimeValue()
|
|
|
|
{
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private sealed class ConstantPoolItemLiveObject : ConstantPoolItem
|
|
|
|
{
|
|
|
|
internal readonly object Value;
|
|
|
|
|
|
|
|
internal ConstantPoolItemLiveObject(object value)
|
|
|
|
{
|
|
|
|
this.Value = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override ConstantType GetConstantType()
|
|
|
|
{
|
|
|
|
return ConstantType.LiveObject;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
internal enum Constant
|
|
|
|
{
|
|
|
|
Utf8 = 1,
|
|
|
|
Integer = 3,
|
|
|
|
Float = 4,
|
|
|
|
Long = 5,
|
|
|
|
Double = 6,
|
|
|
|
Class = 7,
|
|
|
|
String = 8,
|
|
|
|
Fieldref = 9,
|
|
|
|
Methodref = 10,
|
|
|
|
InterfaceMethodref = 11,
|
|
|
|
NameAndType = 12,
|
|
|
|
MethodHandle = 15,
|
|
|
|
MethodType = 16,
|
|
|
|
InvokeDynamic = 18,
|
|
|
|
}
|
|
|
|
|
|
|
|
internal abstract class FieldOrMethod : IEquatable<FieldOrMethod>
|
|
|
|
{
|
|
|
|
// Note that Modifiers is a ushort, so it combines nicely with the following ushort field
|
|
|
|
protected Modifiers access_flags;
|
|
|
|
protected ushort flags;
|
|
|
|
private string name;
|
|
|
|
private string descriptor;
|
|
|
|
protected string signature;
|
|
|
|
protected object[] annotations;
|
2014-10-04 11:27:48 +01:00
|
|
|
protected byte[] runtimeVisibleTypeAnnotations;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
internal FieldOrMethod(ClassFile classFile, string[] utf8_cp, BigEndianBinaryReader br)
|
|
|
|
{
|
|
|
|
access_flags = (Modifiers)br.ReadUInt16();
|
|
|
|
name = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()));
|
|
|
|
descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16());
|
|
|
|
ValidateSig(classFile, descriptor);
|
|
|
|
descriptor = String.Intern(descriptor.Replace('/', '.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected abstract void ValidateSig(ClassFile classFile, string descriptor);
|
|
|
|
|
|
|
|
internal string Name
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string Signature
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return descriptor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal object[] Annotations
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return annotations;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string GenericSignature
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return signature;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Modifiers Modifiers
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (Modifiers)access_flags;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsAbstract
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Abstract) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsFinal
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Final) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsPublic
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Public) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsPrivate
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Private) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsProtected
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Protected) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsStatic
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Static) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsSynchronized
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Synchronized) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsVolatile
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Volatile) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsTransient
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Transient) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsNative
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Native) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsEnum
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Enum) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool DeprecatedAttribute
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (flags & FLAG_MASK_DEPRECATED) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsInternal
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (flags & FLAG_MASK_INTERNAL) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal byte[] RuntimeVisibleTypeAnnotations
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return runtimeVisibleTypeAnnotations;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
public sealed override int GetHashCode()
|
|
|
|
{
|
|
|
|
return name.GetHashCode() ^ descriptor.GetHashCode();
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool Equals(FieldOrMethod other)
|
|
|
|
{
|
|
|
|
return ReferenceEquals(name, other.name) && ReferenceEquals(descriptor, other.descriptor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class Field : FieldOrMethod
|
|
|
|
{
|
|
|
|
private object constantValue;
|
|
|
|
private string[] propertyGetterSetter;
|
|
|
|
|
|
|
|
internal Field(ClassFile classFile, string[] utf8_cp, BigEndianBinaryReader br) : base(classFile, utf8_cp, br)
|
|
|
|
{
|
|
|
|
if((IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected)
|
|
|
|
|| (IsFinal && IsVolatile)
|
|
|
|
|| (classFile.IsInterface && (!IsPublic || !IsStatic || !IsFinal || IsTransient)))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Illegal field modifiers: 0x{1:X})", classFile.Name, access_flags);
|
|
|
|
}
|
|
|
|
int attributes_count = br.ReadUInt16();
|
|
|
|
for(int i = 0; i < attributes_count; i++)
|
|
|
|
{
|
|
|
|
switch(classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()))
|
|
|
|
{
|
|
|
|
case "Deprecated":
|
|
|
|
if(br.ReadUInt32() != 0)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid Deprecated attribute length");
|
|
|
|
}
|
|
|
|
flags |= FLAG_MASK_DEPRECATED;
|
|
|
|
break;
|
|
|
|
case "ConstantValue":
|
|
|
|
{
|
|
|
|
if(br.ReadUInt32() != 2)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid ConstantValue attribute length");
|
|
|
|
}
|
|
|
|
ushort index = br.ReadUInt16();
|
|
|
|
try
|
|
|
|
{
|
|
|
|
switch(Signature)
|
|
|
|
{
|
|
|
|
case "I":
|
|
|
|
constantValue = classFile.GetConstantPoolConstantInteger(index);
|
|
|
|
break;
|
|
|
|
case "S":
|
|
|
|
constantValue = (short)classFile.GetConstantPoolConstantInteger(index);
|
|
|
|
break;
|
|
|
|
case "B":
|
|
|
|
constantValue = (byte)classFile.GetConstantPoolConstantInteger(index);
|
|
|
|
break;
|
|
|
|
case "C":
|
|
|
|
constantValue = (char)classFile.GetConstantPoolConstantInteger(index);
|
|
|
|
break;
|
|
|
|
case "Z":
|
|
|
|
constantValue = classFile.GetConstantPoolConstantInteger(index) != 0;
|
|
|
|
break;
|
|
|
|
case "J":
|
|
|
|
constantValue = classFile.GetConstantPoolConstantLong(index);
|
|
|
|
break;
|
|
|
|
case "F":
|
|
|
|
constantValue = classFile.GetConstantPoolConstantFloat(index);
|
|
|
|
break;
|
|
|
|
case "D":
|
|
|
|
constantValue = classFile.GetConstantPoolConstantDouble(index);
|
|
|
|
break;
|
|
|
|
case "Ljava.lang.String;":
|
|
|
|
constantValue = classFile.GetConstantPoolConstantString(index);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ClassFormatError("{0} (Invalid signature for constant)", classFile.Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(InvalidCastException)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name);
|
|
|
|
}
|
|
|
|
catch(IndexOutOfRangeException)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name);
|
|
|
|
}
|
|
|
|
catch(InvalidOperationException)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name);
|
|
|
|
}
|
|
|
|
catch(NullReferenceException)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "Signature":
|
|
|
|
if(classFile.MajorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
if(br.ReadUInt32() != 2)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Signature attribute has incorrect length");
|
|
|
|
}
|
|
|
|
signature = classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16());
|
|
|
|
break;
|
|
|
|
case "RuntimeVisibleAnnotations":
|
|
|
|
if(classFile.MajorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
annotations = ReadAnnotations(br, classFile, utf8_cp);
|
|
|
|
break;
|
|
|
|
case "RuntimeInvisibleAnnotations":
|
|
|
|
if(classFile.MajorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
foreach(object[] annot in ReadAnnotations(br, classFile, utf8_cp))
|
|
|
|
{
|
|
|
|
if(annot[1].Equals("Likvm/lang/Property;"))
|
|
|
|
{
|
|
|
|
DecodePropertyAnnotation(classFile, annot);
|
|
|
|
}
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
else if(annot[1].Equals("Likvm/lang/Internal;"))
|
|
|
|
{
|
|
|
|
this.access_flags &= ~Modifiers.AccessMask;
|
|
|
|
flags |= FLAG_MASK_INTERNAL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
break;
|
2014-10-04 11:27:48 +01:00
|
|
|
case "RuntimeVisibleTypeAnnotations":
|
|
|
|
if (classFile.MajorVersion < 52)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
classFile.CreateUtf8ConstantPoolItems(utf8_cp);
|
|
|
|
runtimeVisibleTypeAnnotations = br.Section(br.ReadUInt32()).ToArray();
|
|
|
|
break;
|
2014-08-13 10:39:27 +01:00
|
|
|
default:
|
|
|
|
br.Skip(br.ReadUInt32());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void DecodePropertyAnnotation(ClassFile classFile, object[] annot)
|
|
|
|
{
|
|
|
|
if(propertyGetterSetter != null)
|
|
|
|
{
|
|
|
|
Tracer.Error(Tracer.ClassLoading, "Ignoring duplicate ikvm.lang.Property annotation on {0}.{1}", classFile.Name, this.Name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
propertyGetterSetter = new string[2];
|
|
|
|
for(int i = 2; i < annot.Length - 1; i += 2)
|
|
|
|
{
|
|
|
|
string value = annot[i + 1] as string;
|
|
|
|
if(value == null)
|
|
|
|
{
|
|
|
|
propertyGetterSetter = null;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(annot[i].Equals("get") && propertyGetterSetter[0] == null)
|
|
|
|
{
|
|
|
|
propertyGetterSetter[0] = value;
|
|
|
|
}
|
|
|
|
else if(annot[i].Equals("set") && propertyGetterSetter[1] == null)
|
|
|
|
{
|
|
|
|
propertyGetterSetter[1] = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
propertyGetterSetter = null;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(propertyGetterSetter == null || propertyGetterSetter[0] == null)
|
|
|
|
{
|
|
|
|
propertyGetterSetter = null;
|
|
|
|
Tracer.Error(Tracer.ClassLoading, "Ignoring malformed ikvm.lang.Property annotation on {0}.{1}", classFile.Name, this.Name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void ValidateSig(ClassFile classFile, string descriptor)
|
|
|
|
{
|
|
|
|
if(!IsValidFieldSig(descriptor))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Field \"{1}\" has invalid signature \"{2}\")", classFile.Name, this.Name, descriptor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal object ConstantValue
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return constantValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsStaticFinalConstant
|
|
|
|
{
|
|
|
|
get { return (access_flags & (Modifiers.Final | Modifiers.Static)) == (Modifiers.Final | Modifiers.Static) && constantValue != null; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsProperty
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return propertyGetterSetter != null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string PropertyGetter
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return propertyGetterSetter[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string PropertySetter
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return propertyGetterSetter[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class Method : FieldOrMethod
|
|
|
|
{
|
|
|
|
private Code code;
|
|
|
|
private string[] exceptions;
|
|
|
|
private LowFreqData low;
|
|
|
|
private MethodParametersEntry[] parameters;
|
|
|
|
|
|
|
|
sealed class LowFreqData
|
|
|
|
{
|
|
|
|
internal object annotationDefault;
|
|
|
|
internal object[][] parameterAnnotations;
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
internal string DllExportName;
|
|
|
|
internal int DllExportOrdinal;
|
2014-10-04 11:27:48 +01:00
|
|
|
internal string InterlockedCompareAndSetField;
|
2014-08-13 10:39:27 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Method(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options, BigEndianBinaryReader br) : base(classFile, utf8_cp, br)
|
|
|
|
{
|
|
|
|
// vmspec 4.6 says that all flags, except ACC_STRICT are ignored on <clinit>
|
|
|
|
// however, since Java 7 it does need to be marked static
|
|
|
|
if(ReferenceEquals(Name, StringConstants.CLINIT) && ReferenceEquals(Signature, StringConstants.SIG_VOID) && (classFile.MajorVersion < 51 || IsStatic))
|
|
|
|
{
|
|
|
|
access_flags &= Modifiers.Strictfp;
|
|
|
|
access_flags |= (Modifiers.Static | Modifiers.Private);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// LAMESPEC: vmspec 4.6 says that abstract methods can not be strictfp (and this makes sense), but
|
|
|
|
// javac (pre 1.5) is broken and marks abstract methods as strictfp (if you put the strictfp on the class)
|
|
|
|
if((ReferenceEquals(Name, StringConstants.INIT) && (IsStatic || IsSynchronized || IsFinal || IsAbstract || IsNative))
|
|
|
|
|| (IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected)
|
|
|
|
|| (IsAbstract && (IsFinal || IsNative || IsPrivate || IsStatic || IsSynchronized))
|
|
|
|
|| (classFile.IsInterface && classFile.MajorVersion <= 51 && (!IsPublic || IsFinal || IsNative || IsSynchronized || !IsAbstract))
|
|
|
|
|| (classFile.IsInterface && classFile.MajorVersion >= 52 && (!(IsPublic || IsPrivate) || IsFinal || IsNative || IsSynchronized)))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Method {0} in class {1} has illegal modifiers: 0x{2:X}", Name, classFile.Name, (int)access_flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int attributes_count = br.ReadUInt16();
|
|
|
|
for(int i = 0; i < attributes_count; i++)
|
|
|
|
{
|
|
|
|
switch(classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()))
|
|
|
|
{
|
|
|
|
case "Deprecated":
|
|
|
|
if(br.ReadUInt32() != 0)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Invalid Deprecated attribute length");
|
|
|
|
}
|
|
|
|
flags |= FLAG_MASK_DEPRECATED;
|
|
|
|
break;
|
|
|
|
case "Code":
|
|
|
|
{
|
|
|
|
if(!code.IsEmpty)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Duplicate Code attribute)", classFile.Name);
|
|
|
|
}
|
|
|
|
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
|
|
|
|
code.Read(classFile, utf8_cp, this, rdr, options);
|
|
|
|
if(!rdr.IsAtEnd)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Code attribute has wrong length)", classFile.Name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "Exceptions":
|
|
|
|
{
|
|
|
|
if(exceptions != null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Duplicate Exceptions attribute)", classFile.Name);
|
|
|
|
}
|
|
|
|
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
|
|
|
|
ushort count = rdr.ReadUInt16();
|
|
|
|
exceptions = new string[count];
|
|
|
|
for(int j = 0; j < count; j++)
|
|
|
|
{
|
|
|
|
exceptions[j] = classFile.GetConstantPoolClass(rdr.ReadUInt16());
|
|
|
|
}
|
|
|
|
if(!rdr.IsAtEnd)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Exceptions attribute has wrong length)", classFile.Name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "Signature":
|
|
|
|
if(classFile.MajorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
if(br.ReadUInt32() != 2)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Signature attribute has incorrect length");
|
|
|
|
}
|
|
|
|
signature = classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16());
|
|
|
|
break;
|
|
|
|
case "RuntimeVisibleAnnotations":
|
|
|
|
if(classFile.MajorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
annotations = ReadAnnotations(br, classFile, utf8_cp);
|
2014-10-04 11:27:48 +01:00
|
|
|
if ((options & ClassFileParseOptions.TrustedAnnotations) != 0)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
foreach(object[] annot in annotations)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
switch((string)annot[1])
|
|
|
|
{
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
case "Lsun/reflect/CallerSensitive;":
|
|
|
|
flags |= FLAG_CALLERSENSITIVE;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case "Ljava/lang/invoke/LambdaForm$Compiled;":
|
|
|
|
flags |= FLAG_LAMBDAFORM_COMPILED;
|
|
|
|
break;
|
|
|
|
case "Ljava/lang/invoke/LambdaForm$Hidden;":
|
|
|
|
flags |= FLAG_LAMBDAFORM_HIDDEN;
|
|
|
|
break;
|
|
|
|
case "Ljava/lang/invoke/ForceInline;":
|
|
|
|
flags |= FLAG_FORCEINLINE;
|
|
|
|
break;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "RuntimeVisibleParameterAnnotations":
|
|
|
|
{
|
|
|
|
if(classFile.MajorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
if(low == null)
|
|
|
|
{
|
|
|
|
low = new LowFreqData();
|
|
|
|
}
|
|
|
|
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
|
|
|
|
byte num_parameters = rdr.ReadByte();
|
|
|
|
low.parameterAnnotations = new object[num_parameters][];
|
|
|
|
for(int j = 0; j < num_parameters; j++)
|
|
|
|
{
|
|
|
|
ushort num_annotations = rdr.ReadUInt16();
|
|
|
|
low.parameterAnnotations[j] = new object[num_annotations];
|
|
|
|
for(int k = 0; k < num_annotations; k++)
|
|
|
|
{
|
|
|
|
low.parameterAnnotations[j][k] = ReadAnnotation(rdr, classFile, utf8_cp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!rdr.IsAtEnd)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (RuntimeVisibleParameterAnnotations attribute has wrong length)", classFile.Name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "AnnotationDefault":
|
|
|
|
{
|
|
|
|
if(classFile.MajorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
if(low == null)
|
|
|
|
{
|
|
|
|
low = new LowFreqData();
|
|
|
|
}
|
|
|
|
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
|
|
|
|
low.annotationDefault = ReadAnnotationElementValue(rdr, classFile, utf8_cp);
|
|
|
|
if(!rdr.IsAtEnd)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (AnnotationDefault attribute has wrong length)", classFile.Name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
case "RuntimeInvisibleAnnotations":
|
|
|
|
if(classFile.MajorVersion < 49)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
foreach(object[] annot in ReadAnnotations(br, classFile, utf8_cp))
|
|
|
|
{
|
|
|
|
if(annot[1].Equals("Likvm/lang/Internal;"))
|
|
|
|
{
|
|
|
|
if (classFile.IsInterface)
|
|
|
|
{
|
|
|
|
StaticCompiler.IssueMessage(Message.InterfaceMethodCantBeInternal, classFile.Name, this.Name, this.Signature);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.access_flags &= ~Modifiers.AccessMask;
|
|
|
|
flags |= FLAG_MASK_INTERNAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(annot[1].Equals("Likvm/lang/DllExport;"))
|
|
|
|
{
|
|
|
|
string name = null;
|
|
|
|
int? ordinal = null;
|
|
|
|
for (int j = 2; j < annot.Length; j += 2)
|
|
|
|
{
|
|
|
|
if (annot[j].Equals("name") && annot[j + 1] is string)
|
|
|
|
{
|
|
|
|
name = (string)annot[j + 1];
|
|
|
|
}
|
|
|
|
else if (annot[j].Equals("ordinal") && annot[j + 1] is int)
|
|
|
|
{
|
|
|
|
ordinal = (int)annot[j + 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (name != null && ordinal != null)
|
|
|
|
{
|
|
|
|
if (!IsStatic)
|
|
|
|
{
|
|
|
|
StaticCompiler.IssueMessage(Message.DllExportMustBeStaticMethod, classFile.Name, this.Name, this.Signature);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (low == null)
|
|
|
|
{
|
|
|
|
low = new LowFreqData();
|
|
|
|
}
|
|
|
|
low.DllExportName = name;
|
|
|
|
low.DllExportOrdinal = ordinal.Value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
if(annot[1].Equals("Likvm/internal/InterlockedCompareAndSet;"))
|
|
|
|
{
|
|
|
|
string field = null;
|
|
|
|
for (int j = 2; j < annot.Length; j += 2)
|
|
|
|
{
|
|
|
|
if (annot[j].Equals("value") && annot[j + 1] is string)
|
|
|
|
{
|
|
|
|
field = (string)annot[j + 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (field != null)
|
|
|
|
{
|
|
|
|
if (low == null)
|
|
|
|
{
|
|
|
|
low = new LowFreqData();
|
|
|
|
}
|
|
|
|
low.InterlockedCompareAndSetField = field;
|
|
|
|
}
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case "MethodParameters":
|
|
|
|
{
|
|
|
|
if(classFile.MajorVersion < 52)
|
|
|
|
{
|
|
|
|
goto default;
|
|
|
|
}
|
|
|
|
if(parameters != null)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Duplicate MethodParameters attribute)", classFile.Name);
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
parameters = ReadMethodParameters(br, utf8_cp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "RuntimeVisibleTypeAnnotations":
|
|
|
|
if (classFile.MajorVersion < 52)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
goto default;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
classFile.CreateUtf8ConstantPoolItems(utf8_cp);
|
|
|
|
runtimeVisibleTypeAnnotations = br.Section(br.ReadUInt32()).ToArray();
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
br.Skip(br.ReadUInt32());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(IsAbstract || IsNative)
|
|
|
|
{
|
|
|
|
if(!code.IsEmpty)
|
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
throw new ClassFormatError("Code attribute in native or abstract methods in class file " + classFile.Name);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(code.IsEmpty)
|
|
|
|
{
|
|
|
|
if(ReferenceEquals(this.Name, StringConstants.CLINIT))
|
|
|
|
{
|
|
|
|
code.verifyError = string.Format("Class {0}, method {1} signature {2}: No Code attribute", classFile.Name, this.Name, this.Signature);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
throw new ClassFormatError("Absent Code attribute in method that is not native or abstract in class file " + classFile.Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
private static MethodParametersEntry[] ReadMethodParameters(BigEndianBinaryReader br, string[] utf8_cp)
|
|
|
|
{
|
|
|
|
uint length = br.ReadUInt32();
|
|
|
|
if(length > 0)
|
|
|
|
{
|
|
|
|
BigEndianBinaryReader rdr = br.Section(length);
|
|
|
|
byte parameters_count = rdr.ReadByte();
|
|
|
|
if(length == 1 + parameters_count * 4)
|
|
|
|
{
|
|
|
|
MethodParametersEntry[] parameters = new MethodParametersEntry[parameters_count];
|
|
|
|
for(int j = 0; j < parameters_count; j++)
|
|
|
|
{
|
|
|
|
ushort name = rdr.ReadUInt16();
|
|
|
|
if(name >= utf8_cp.Length || (name != 0 && utf8_cp[name] == null))
|
|
|
|
{
|
|
|
|
return MethodParametersEntry.Malformed;
|
|
|
|
}
|
|
|
|
parameters[j].name = utf8_cp[name];
|
|
|
|
parameters[j].flags = rdr.ReadUInt16();
|
|
|
|
}
|
|
|
|
return parameters;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new ClassFormatError("Invalid MethodParameters method attribute length " + length + " in class file");
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
protected override void ValidateSig(ClassFile classFile, string descriptor)
|
|
|
|
{
|
|
|
|
if(!IsValidMethodSig(descriptor))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Method \"{1}\" has invalid signature \"{2}\")", classFile.Name, this.Name, descriptor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsStrictfp
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & Modifiers.Strictfp) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal bool IsVirtual
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (access_flags & (Modifiers.Static | Modifiers.Private)) == 0
|
|
|
|
&& !IsConstructor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
// Is this the <clinit>()V method?
|
|
|
|
internal bool IsClassInitializer
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return ReferenceEquals(Name, StringConstants.CLINIT) && ReferenceEquals(Signature, StringConstants.SIG_VOID) && IsStatic;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsConstructor
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return ReferenceEquals(Name, StringConstants.INIT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
internal bool IsCallerSensitive
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (flags & FLAG_CALLERSENSITIVE) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal bool IsLambdaFormCompiled
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (flags & FLAG_LAMBDAFORM_COMPILED) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsLambdaFormHidden
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (flags & FLAG_LAMBDAFORM_HIDDEN) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsForceInline
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (flags & FLAG_FORCEINLINE) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
internal string[] ExceptionsAttribute
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return exceptions;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal object[][] ParameterAnnotations
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return low == null ? null : low.parameterAnnotations;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal object AnnotationDefault
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return low == null ? null : low.annotationDefault;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if STATIC_COMPILER
|
|
|
|
internal string DllExportName
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return low == null ? null : low.DllExportName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int DllExportOrdinal
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return low == null ? -1 : low.DllExportOrdinal;
|
|
|
|
}
|
|
|
|
}
|
2014-10-04 11:27:48 +01:00
|
|
|
|
|
|
|
internal string InterlockedCompareAndSetField
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return low == null ? null : low.InterlockedCompareAndSetField;
|
|
|
|
}
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
internal string VerifyError
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.verifyError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// maps argument 'slot' (as encoded in the xload/xstore instructions) into the ordinal
|
|
|
|
internal int[] ArgMap
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.argmap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int MaxStack
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.max_stack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int MaxLocals
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.max_locals;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Instruction[] Instructions
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.instructions;
|
|
|
|
}
|
|
|
|
set
|
|
|
|
{
|
|
|
|
code.instructions = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ExceptionTableEntry[] ExceptionTable
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.exception_table;
|
|
|
|
}
|
|
|
|
set
|
|
|
|
{
|
|
|
|
code.exception_table = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal LineNumberTableEntry[] LineNumberTableAttribute
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.lineNumberTable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal LocalVariableTableEntry[] LocalVariableTableAttribute
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.localVariableTable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal MethodParametersEntry[] MethodParameters
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return parameters;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 11:27:48 +01:00
|
|
|
internal bool MalformedMethodParameters
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return parameters == MethodParametersEntry.Malformed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
internal bool HasJsr
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return code.hasJsr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private struct Code
|
|
|
|
{
|
|
|
|
internal bool hasJsr;
|
|
|
|
internal string verifyError;
|
|
|
|
internal ushort max_stack;
|
|
|
|
internal ushort max_locals;
|
|
|
|
internal Instruction[] instructions;
|
|
|
|
internal ExceptionTableEntry[] exception_table;
|
|
|
|
internal int[] argmap;
|
|
|
|
internal LineNumberTableEntry[] lineNumberTable;
|
|
|
|
internal LocalVariableTableEntry[] localVariableTable;
|
|
|
|
|
|
|
|
internal void Read(ClassFile classFile, string[] utf8_cp, Method method, BigEndianBinaryReader br, ClassFileParseOptions options)
|
|
|
|
{
|
|
|
|
max_stack = br.ReadUInt16();
|
|
|
|
max_locals = br.ReadUInt16();
|
|
|
|
uint code_length = br.ReadUInt32();
|
2014-10-04 11:27:48 +01:00
|
|
|
if(code_length == 0 || code_length > 65535)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2014-10-04 11:27:48 +01:00
|
|
|
throw new ClassFormatError("Invalid method Code length {1} in class file {0}", classFile.Name, code_length);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
Instruction[] instructions = new Instruction[code_length + 1];
|
|
|
|
int basePosition = br.Position;
|
|
|
|
int instructionIndex = 0;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
BigEndianBinaryReader rdr = br.Section(code_length);
|
|
|
|
while(!rdr.IsAtEnd)
|
|
|
|
{
|
|
|
|
instructions[instructionIndex].Read((ushort)(rdr.Position - basePosition), rdr, classFile);
|
|
|
|
hasJsr |= instructions[instructionIndex].NormalizedOpCode == NormalizedByteCode.__jsr;
|
|
|
|
instructionIndex++;
|
|
|
|
}
|
|
|
|
// we add an additional nop instruction to make it easier for consumers of the code array
|
|
|
|
instructions[instructionIndex++].SetTermNop((ushort)(rdr.Position - basePosition));
|
|
|
|
}
|
|
|
|
catch(ClassFormatError x)
|
|
|
|
{
|
|
|
|
// any class format errors in the code block are actually verify errors
|
|
|
|
verifyError = x.Message;
|
|
|
|
}
|
|
|
|
this.instructions = new Instruction[instructionIndex];
|
|
|
|
Array.Copy(instructions, 0, this.instructions, 0, instructionIndex);
|
|
|
|
// build the pcIndexMap
|
|
|
|
int[] pcIndexMap = new int[this.instructions[instructionIndex - 1].PC + 1];
|
|
|
|
for(int i = 0; i < pcIndexMap.Length; i++)
|
|
|
|
{
|
|
|
|
pcIndexMap[i] = -1;
|
|
|
|
}
|
|
|
|
for(int i = 0; i < instructionIndex - 1; i++)
|
|
|
|
{
|
|
|
|
pcIndexMap[this.instructions[i].PC] = i;
|
|
|
|
}
|
|
|
|
// convert branch offsets to indexes
|
|
|
|
for(int i = 0; i < instructionIndex - 1; i++)
|
|
|
|
{
|
|
|
|
switch(this.instructions[i].NormalizedOpCode)
|
|
|
|
{
|
|
|
|
case NormalizedByteCode.__ifeq:
|
|
|
|
case NormalizedByteCode.__ifne:
|
|
|
|
case NormalizedByteCode.__iflt:
|
|
|
|
case NormalizedByteCode.__ifge:
|
|
|
|
case NormalizedByteCode.__ifgt:
|
|
|
|
case NormalizedByteCode.__ifle:
|
|
|
|
case NormalizedByteCode.__if_icmpeq:
|
|
|
|
case NormalizedByteCode.__if_icmpne:
|
|
|
|
case NormalizedByteCode.__if_icmplt:
|
|
|
|
case NormalizedByteCode.__if_icmpge:
|
|
|
|
case NormalizedByteCode.__if_icmpgt:
|
|
|
|
case NormalizedByteCode.__if_icmple:
|
|
|
|
case NormalizedByteCode.__if_acmpeq:
|
|
|
|
case NormalizedByteCode.__if_acmpne:
|
|
|
|
case NormalizedByteCode.__ifnull:
|
|
|
|
case NormalizedByteCode.__ifnonnull:
|
|
|
|
case NormalizedByteCode.__goto:
|
|
|
|
case NormalizedByteCode.__jsr:
|
|
|
|
this.instructions[i].SetTargetIndex(pcIndexMap[this.instructions[i].Arg1 + this.instructions[i].PC]);
|
|
|
|
break;
|
|
|
|
case NormalizedByteCode.__tableswitch:
|
|
|
|
case NormalizedByteCode.__lookupswitch:
|
|
|
|
this.instructions[i].MapSwitchTargets(pcIndexMap);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// read exception table
|
|
|
|
ushort exception_table_length = br.ReadUInt16();
|
|
|
|
exception_table = new ExceptionTableEntry[exception_table_length];
|
|
|
|
for(int i = 0; i < exception_table_length; i++)
|
|
|
|
{
|
|
|
|
ushort start_pc = br.ReadUInt16();
|
|
|
|
ushort end_pc = br.ReadUInt16();
|
|
|
|
ushort handler_pc = br.ReadUInt16();
|
|
|
|
ushort catch_type = br.ReadUInt16();
|
|
|
|
if(start_pc >= end_pc
|
|
|
|
|| end_pc > code_length
|
|
|
|
|| handler_pc >= code_length
|
|
|
|
|| (catch_type != 0 && !classFile.SafeIsConstantPoolClass(catch_type)))
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Illegal exception table: {0}.{1}{2}", classFile.Name, method.Name, method.Signature);
|
|
|
|
}
|
|
|
|
classFile.MarkLinkRequiredConstantPoolItem(catch_type);
|
|
|
|
// if start_pc, end_pc or handler_pc is invalid (i.e. doesn't point to the start of an instruction),
|
|
|
|
// the index will be -1 and this will be handled by the verifier
|
|
|
|
int startIndex = pcIndexMap[start_pc];
|
|
|
|
int endIndex;
|
|
|
|
if (end_pc == code_length)
|
|
|
|
{
|
|
|
|
// it is legal for end_pc to point to just after the last instruction,
|
|
|
|
// but since there isn't an entry in our pcIndexMap for that, we have
|
|
|
|
// a special case for this
|
|
|
|
endIndex = instructionIndex - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
endIndex = pcIndexMap[end_pc];
|
|
|
|
}
|
|
|
|
int handlerIndex = pcIndexMap[handler_pc];
|
|
|
|
exception_table[i] = new ExceptionTableEntry(startIndex, endIndex, handlerIndex, catch_type, i);
|
|
|
|
}
|
|
|
|
ushort attributes_count = br.ReadUInt16();
|
|
|
|
for(int i = 0; i < attributes_count; i++)
|
|
|
|
{
|
|
|
|
switch(classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()))
|
|
|
|
{
|
|
|
|
case "LineNumberTable":
|
|
|
|
if((options & ClassFileParseOptions.LineNumberTable) != 0)
|
|
|
|
{
|
|
|
|
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
|
|
|
|
int count = rdr.ReadUInt16();
|
|
|
|
lineNumberTable = new LineNumberTableEntry[count];
|
|
|
|
for(int j = 0; j < count; j++)
|
|
|
|
{
|
|
|
|
lineNumberTable[j].start_pc = rdr.ReadUInt16();
|
|
|
|
lineNumberTable[j].line_number = rdr.ReadUInt16();
|
|
|
|
if(lineNumberTable[j].start_pc >= code_length)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (LineNumberTable has invalid pc)", classFile.Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!rdr.IsAtEnd)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (LineNumberTable attribute has wrong length)", classFile.Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
br.Skip(br.ReadUInt32());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "LocalVariableTable":
|
|
|
|
if((options & ClassFileParseOptions.LocalVariableTable) != 0)
|
|
|
|
{
|
|
|
|
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
|
|
|
|
int count = rdr.ReadUInt16();
|
|
|
|
localVariableTable = new LocalVariableTableEntry[count];
|
|
|
|
for(int j = 0; j < count; j++)
|
|
|
|
{
|
|
|
|
localVariableTable[j].start_pc = rdr.ReadUInt16();
|
|
|
|
localVariableTable[j].length = rdr.ReadUInt16();
|
|
|
|
localVariableTable[j].name = classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16());
|
|
|
|
localVariableTable[j].descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16()).Replace('/', '.');
|
|
|
|
localVariableTable[j].index = rdr.ReadUInt16();
|
|
|
|
}
|
|
|
|
// NOTE we're intentionally not checking that we're at the end of the section
|
|
|
|
// (optional attributes shouldn't cause ClassFormatError)
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
br.Skip(br.ReadUInt32());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
br.Skip(br.ReadUInt32());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// build the argmap
|
|
|
|
string sig = method.Signature;
|
|
|
|
List<int> args = new List<int>();
|
|
|
|
int pos = 0;
|
|
|
|
if(!method.IsStatic)
|
|
|
|
{
|
|
|
|
args.Add(pos++);
|
|
|
|
}
|
|
|
|
for(int i = 1; sig[i] != ')'; i++)
|
|
|
|
{
|
|
|
|
args.Add(pos++);
|
|
|
|
switch(sig[i])
|
|
|
|
{
|
|
|
|
case 'L':
|
|
|
|
i = sig.IndexOf(';', i);
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
case 'J':
|
|
|
|
args.Add(-1);
|
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
{
|
|
|
|
while(sig[i] == '[')
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if(sig[i] == 'L')
|
|
|
|
{
|
|
|
|
i = sig.IndexOf(';', i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
argmap = args.ToArray();
|
|
|
|
if(args.Count > max_locals)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("{0} (Arguments can't fit into locals)", classFile.Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsEmpty
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return instructions == null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class ExceptionTableEntry
|
|
|
|
{
|
|
|
|
internal readonly int startIndex;
|
|
|
|
internal readonly int endIndex;
|
|
|
|
internal readonly int handlerIndex;
|
|
|
|
internal readonly ushort catch_type;
|
|
|
|
internal readonly int ordinal;
|
|
|
|
internal readonly bool isFinally;
|
|
|
|
|
|
|
|
internal ExceptionTableEntry(int startIndex, int endIndex, int handlerIndex, ushort catch_type, int ordinal)
|
|
|
|
: this(startIndex, endIndex, handlerIndex, catch_type, ordinal, false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ExceptionTableEntry(int startIndex, int endIndex, int handlerIndex, ushort catch_type, int ordinal, bool isFinally)
|
|
|
|
{
|
|
|
|
this.startIndex = startIndex;
|
|
|
|
this.endIndex = endIndex;
|
|
|
|
this.handlerIndex = handlerIndex;
|
|
|
|
this.catch_type = catch_type;
|
|
|
|
this.ordinal = ordinal;
|
|
|
|
this.isFinally = isFinally;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Flags]
|
|
|
|
internal enum InstructionFlags : byte
|
|
|
|
{
|
|
|
|
Reachable = 1,
|
|
|
|
Processed = 2,
|
|
|
|
BranchTarget = 4,
|
|
|
|
}
|
|
|
|
|
|
|
|
internal struct Instruction
|
|
|
|
{
|
|
|
|
private ushort pc;
|
|
|
|
private NormalizedByteCode normopcode;
|
|
|
|
private int arg1;
|
|
|
|
private short arg2;
|
|
|
|
private SwitchEntry[] switch_entries;
|
|
|
|
|
|
|
|
struct SwitchEntry
|
|
|
|
{
|
|
|
|
internal int value;
|
|
|
|
internal int target;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void SetHardError(HardError error, int messageId)
|
|
|
|
{
|
|
|
|
normopcode = NormalizedByteCode.__static_error;
|
|
|
|
arg2 = (short)error;
|
|
|
|
arg1 = messageId;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal HardError HardError
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return (HardError)arg2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int HandlerIndex
|
|
|
|
{
|
|
|
|
get { return (ushort)arg2; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int HardErrorMessageId
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return arg1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void PatchOpCode(NormalizedByteCode bc)
|
|
|
|
{
|
|
|
|
this.normopcode = bc;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void PatchOpCode(NormalizedByteCode bc, int arg1)
|
|
|
|
{
|
|
|
|
this.normopcode = bc;
|
|
|
|
this.arg1 = arg1;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void PatchOpCode(NormalizedByteCode bc, int arg1, short arg2)
|
|
|
|
{
|
|
|
|
this.normopcode = bc;
|
|
|
|
this.arg1 = arg1;
|
|
|
|
this.arg2 = arg2;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void SetPC(int pc)
|
|
|
|
{
|
|
|
|
this.pc = (ushort)pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void SetTargetIndex(int targetIndex)
|
|
|
|
{
|
|
|
|
this.arg1 = targetIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void SetTermNop(ushort pc)
|
|
|
|
{
|
|
|
|
// TODO what happens if we already have exactly the maximum number of instructions?
|
|
|
|
this.pc = pc;
|
|
|
|
this.normopcode = NormalizedByteCode.__nop;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void MapSwitchTargets(int[] pcIndexMap)
|
|
|
|
{
|
|
|
|
arg1 = pcIndexMap[arg1 + pc];
|
|
|
|
for (int i = 0; i < switch_entries.Length; i++)
|
|
|
|
{
|
|
|
|
switch_entries[i].target = pcIndexMap[switch_entries[i].target + pc];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void Read(ushort pc, BigEndianBinaryReader br, ClassFile classFile)
|
|
|
|
{
|
|
|
|
this.pc = pc;
|
|
|
|
ByteCode bc = (ByteCode)br.ReadByte();
|
|
|
|
switch(ByteCodeMetaData.GetMode(bc))
|
|
|
|
{
|
|
|
|
case ByteCodeMode.Simple:
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Constant_1:
|
|
|
|
arg1 = br.ReadByte();
|
|
|
|
classFile.MarkLinkRequiredConstantPoolItem(arg1);
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Local_1:
|
|
|
|
arg1 = br.ReadByte();
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Constant_2:
|
|
|
|
arg1 = br.ReadUInt16();
|
|
|
|
classFile.MarkLinkRequiredConstantPoolItem(arg1);
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Branch_2:
|
|
|
|
arg1 = br.ReadInt16();
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Branch_4:
|
|
|
|
arg1 = br.ReadInt32();
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Constant_2_1_1:
|
|
|
|
arg1 = br.ReadUInt16();
|
|
|
|
classFile.MarkLinkRequiredConstantPoolItem(arg1);
|
|
|
|
arg2 = br.ReadByte();
|
|
|
|
if(br.ReadByte() != 0)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("invokeinterface filler must be zero");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Immediate_1:
|
|
|
|
arg1 = br.ReadSByte();
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Immediate_2:
|
|
|
|
arg1 = br.ReadInt16();
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Local_1_Immediate_1:
|
|
|
|
arg1 = br.ReadByte();
|
|
|
|
arg2 = br.ReadSByte();
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Constant_2_Immediate_1:
|
|
|
|
arg1 = br.ReadUInt16();
|
|
|
|
classFile.MarkLinkRequiredConstantPoolItem(arg1);
|
|
|
|
arg2 = br.ReadSByte();
|
|
|
|
break;
|
|
|
|
case ByteCodeMode.Tableswitch:
|
|
|
|
{
|
|
|
|
// skip the padding
|
|
|
|
uint p = pc + 1u;
|
|
|
|
uint align = ((p + 3) & 0x7ffffffc) - p;
|
|
|
|
br.Skip(align);
|
|
|
|
int default_offset = br.ReadInt32();
|
|
|
|
this.arg1 = default_offset;
|
|
|
|
int low = br.ReadInt32();
|
|
|
|
int high = br.ReadInt32();
|
|
|
|
if(low > high || high > 16384L + low)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Incorrect tableswitch");
|
|
|
|
}
|
|
|
|
SwitchEntry[] entries = new SwitchEntry[high - low + 1];
|
|
|
|
for(int i = low; i < high; i++)
|
|
|
|
{
|
|
|
|
entries[i - low].value = i;
|
|
|
|
entries[i - low].target = br.ReadInt32();
|
|
|
|
}
|
|
|
|
// do the last entry outside the loop, to avoid overflowing "i", if high == int.MaxValue
|
|
|
|
entries[high - low].value = high;
|
|
|
|
entries[high - low].target = br.ReadInt32();
|
|
|
|
this.switch_entries = entries;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ByteCodeMode.Lookupswitch:
|
|
|
|
{
|
|
|
|
// skip the padding
|
|
|
|
uint p = pc + 1u;
|
|
|
|
uint align = ((p + 3) & 0x7ffffffc) - p;
|
|
|
|
br.Skip(align);
|
|
|
|
int default_offset = br.ReadInt32();
|
|
|
|
this.arg1 = default_offset;
|
|
|
|
int count = br.ReadInt32();
|
|
|
|
if(count < 0 || count > 16384)
|
|
|
|
{
|
|
|
|
throw new ClassFormatError("Incorrect lookupswitch");
|
|
|
|
}
|
|
|
|
SwitchEntry[] entries = new SwitchEntry[count];
|
|
|
|
for(int i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
entries[i].value = br.ReadInt32();
|
|
|
|
entries[i].target = br.ReadInt32();
|
|
|
|
}
|
|
|
|
this.switch_entries = entries;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ByteCodeMode.WidePrefix:
|
|
|
|
bc = (ByteCode)br.ReadByte();
|
|
|
|
// NOTE the PC of a wide instruction is actually the PC of the
|
|
|
|
// wide prefix, not the following instruction (vmspec 4.9.2)
|
|
|
|
switch(ByteCodeMetaData.GetWideMode(bc))
|
|
|
|
{
|
|
|
|
case ByteCodeModeWide.Local_2:
|
|
|
|
arg1 = br.ReadUInt16();
|
|
|
|
break;
|
|
|
|
case ByteCodeModeWide.Local_2_Immediate_2:
|
|
|
|
arg1 = br.ReadUInt16();
|
|
|
|
arg2 = br.ReadInt16();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ClassFormatError("Invalid wide prefix on opcode: {0}", bc);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ClassFormatError("Invalid opcode: {0}", bc);
|
|
|
|
}
|
|
|
|
this.normopcode = ByteCodeMetaData.GetNormalizedByteCode(bc);
|
|
|
|
arg1 = ByteCodeMetaData.GetArg(bc, arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int PC
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return pc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal NormalizedByteCode NormalizedOpCode
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return normopcode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int Arg1
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return arg1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int TargetIndex
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return arg1;
|
|
|
|
}
|
|
|
|
set
|
|
|
|
{
|
|
|
|
arg1 = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int Arg2
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return arg2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int NormalizedArg1
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return arg1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int DefaultTarget
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return arg1;
|
|
|
|
}
|
|
|
|
set
|
|
|
|
{
|
|
|
|
arg1 = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int SwitchEntryCount
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return switch_entries.Length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int GetSwitchValue(int i)
|
|
|
|
{
|
|
|
|
return switch_entries[i].value;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int GetSwitchTargetIndex(int i)
|
|
|
|
{
|
|
|
|
return switch_entries[i].target;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void SetSwitchTargets(int[] targets)
|
|
|
|
{
|
|
|
|
SwitchEntry[] newEntries = (SwitchEntry[])switch_entries.Clone();
|
|
|
|
for (int i = 0; i < newEntries.Length; i++)
|
|
|
|
{
|
|
|
|
newEntries[i].target = targets[i];
|
|
|
|
}
|
|
|
|
switch_entries = newEntries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal struct LineNumberTableEntry
|
|
|
|
{
|
|
|
|
internal ushort start_pc;
|
|
|
|
internal ushort line_number;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal struct LocalVariableTableEntry
|
|
|
|
{
|
|
|
|
internal ushort start_pc;
|
|
|
|
internal ushort length;
|
|
|
|
internal string name;
|
|
|
|
internal string descriptor;
|
|
|
|
internal ushort index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Field GetField(string name, string sig)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < fields.Length; i++)
|
|
|
|
{
|
|
|
|
if (fields[i].Name == name && fields[i].Signature == sig)
|
|
|
|
{
|
|
|
|
return fields[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool HasSerialVersionUID
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
Field field = GetField("serialVersionUID", "J");
|
|
|
|
return field != null && field.IsStatic && field.IsFinal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|