diff options
author | jfrijters <jfrijters> | 2014-06-24 16:06:44 +0400 |
---|---|---|
committer | jfrijters <jfrijters> | 2014-06-24 16:06:44 +0400 |
commit | 3da3a2d5706e166961c878e8d55f3f1b96522742 (patch) | |
tree | 5d73c72c63635039dc8ec518e7e7244f4a50bae3 | |
parent | 6e4fc8e4cea6f5ed1eef32d58c449f510bd8867e (diff) |
Updated ldc <methodhandle> error behavior to match Java 8.
-rw-r--r-- | runtime/ClassFile.cs | 2 | ||||
-rw-r--r-- | runtime/compiler.cs | 8 | ||||
-rw-r--r-- | runtime/verifier.cs | 134 |
3 files changed, 104 insertions, 40 deletions
diff --git a/runtime/ClassFile.cs b/runtime/ClassFile.cs index 95286a0f..bdb51002 100644 --- a/runtime/ClassFile.cs +++ b/runtime/ClassFile.cs @@ -39,8 +39,6 @@ namespace IKVM.Internal NoSuchMethodError, LinkageError, // "exceptions" that are wrapped in an IncompatibleClassChangeError - NoSuchFieldException, - NoSuchMethodException, IllegalAccessException, // if an error is added here, it must also be added to MethodAnalyzer.SetHardError() } diff --git a/runtime/compiler.cs b/runtime/compiler.cs index 2ec195c7..b8e0f0ac 100644 --- a/runtime/compiler.cs +++ b/runtime/compiler.cs @@ -2742,14 +2742,6 @@ sealed class Compiler case HardError.NoSuchMethodError: exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchMethodError"); break; - case HardError.NoSuchFieldException: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchFieldException"); - wrapIncompatibleClassChangeError = true; - break; - case HardError.NoSuchMethodException: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchMethodException"); - wrapIncompatibleClassChangeError = true; - break; case HardError.IllegalAccessException: exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IllegalAccessException"); wrapIncompatibleClassChangeError = true; diff --git a/runtime/verifier.cs b/runtime/verifier.cs index 81b47929..c0168e79 100644 --- a/runtime/verifier.cs +++ b/runtime/verifier.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2013 Jeroen Frijters + Copyright (C) 2002-2014 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -1160,6 +1160,7 @@ sealed class MethodAnalyzer private readonly static TypeWrapper DoubleArrayType; private readonly static TypeWrapper LongArrayType; private readonly static TypeWrapper java_lang_ThreadDeath; + private readonly TypeWrapper host; // used to by Unsafe.defineAnonymousClass() to provide access to private members of the host private readonly TypeWrapper wrapper; private readonly MethodWrapper mw; private readonly ClassFile classFile; @@ -1184,13 +1185,14 @@ sealed class MethodAnalyzer java_lang_ThreadDeath = ClassLoaderWrapper.LoadClassCritical("java.lang.ThreadDeath"); } - internal MethodAnalyzer(TypeWrapper wrapper, MethodWrapper mw, ClassFile classFile, ClassFile.Method method, ClassLoaderWrapper classLoader) + internal MethodAnalyzer(TypeWrapper host, TypeWrapper wrapper, MethodWrapper mw, ClassFile classFile, ClassFile.Method method, ClassLoaderWrapper classLoader) { if(method.VerifyError != null) { throw new VerifyError(method.VerifyError); } + this.host = host; this.wrapper = wrapper; this.mw = mw; this.classFile = classFile; @@ -1276,6 +1278,7 @@ sealed class MethodAnalyzer case ClassFile.ConstantType.Integer: case ClassFile.ConstantType.Long: case ClassFile.ConstantType.String: + case ClassFile.ConstantType.LiveObject: code[i].PatchOpCode(NormalizedByteCode.__ldc_nothrow); break; } @@ -1640,6 +1643,9 @@ sealed class MethodAnalyzer case ClassFile.ConstantType.String: s.PushType(CoreClasses.java.lang.String.Wrapper); break; + case ClassFile.ConstantType.LiveObject: + s.PushType(CoreClasses.java.lang.Object.Wrapper); + break; case ClassFile.ConstantType.Class: if(classFile.MajorVersion < 49) { @@ -1669,8 +1675,11 @@ sealed class MethodAnalyzer case NormalizedByteCode.__dynamic_invokespecial: case NormalizedByteCode.__dynamic_invokeinterface: case NormalizedByteCode.__dynamic_invokestatic: + case NormalizedByteCode.__privileged_invokevirtual: + case NormalizedByteCode.__privileged_invokespecial: + case NormalizedByteCode.__privileged_invokestatic: case NormalizedByteCode.__methodhandle_invoke: - case NormalizedByteCode.__methodhandle_invokeexact: + case NormalizedByteCode.__methodhandle_link: { ClassFile.ConstantPoolItemMI cpi = GetMethodref(instr.Arg1); TypeWrapper retType = cpi.GetRetType(); @@ -2406,9 +2415,21 @@ sealed class MethodAnalyzer StackState stack = new StackState(state[index]); NormalizedByteCode invoke = method.Instructions[index].NormalizedOpCode; ClassFile.ConstantPoolItemMI cpi = GetMethodref(method.Instructions[index].Arg1); - if (invoke == NormalizedByteCode.__invokestatic && classFile.MajorVersion >= 52) + if ((invoke == NormalizedByteCode.__invokestatic || invoke == NormalizedByteCode.__invokespecial) && classFile.MajorVersion >= 52) { - // invokestatic may be used to invoke interface methods in Java 8 + // invokestatic and invokespecial may be used to invoke interface methods in Java 8 + // but invokespecial can only invoke methods in the current interface or a directly implemented interface + if (invoke == NormalizedByteCode.__invokespecial && cpi is ClassFile.ConstantPoolItemInterfaceMethodref) + { + if (cpi.GetClassType() == host) + { + // ok + } + else if (cpi.GetClassType() != wrapper && Array.IndexOf(wrapper.Interfaces, cpi.GetClassType()) == -1) + { + throw new VerifyError("Bad invokespecial instruction: interface method reference is in an indirect superinterface."); + } + } } else if ((cpi is ClassFile.ConstantPoolItemInterfaceMethodref) != (invoke == NormalizedByteCode.__invokeinterface)) { @@ -2482,11 +2503,35 @@ sealed class MethodAnalyzer // for invokespecial we also need to make sure we're calling ourself or a base class if (invoke == NormalizedByteCode.__invokespecial) { - if (!VerifierTypeWrapper.IsNullOrUnloadable(refType) && !refType.IsSubTypeOf(wrapper)) + if (VerifierTypeWrapper.IsNullOrUnloadable(refType)) + { + // ok + } + else if (refType.IsSubTypeOf(wrapper)) + { + // ok + } + else if (host != null && refType.IsSubTypeOf(host)) + { + // ok + } + else { throw new VerifyError("Incompatible target object for invokespecial"); } - if (!targetType.IsUnloadable && !wrapper.IsSubTypeOf(targetType)) + if (targetType.IsUnloadable) + { + // ok + } + else if (wrapper.IsSubTypeOf(targetType)) + { + // ok + } + else if (host != null && host.IsSubTypeOf(targetType)) + { + // ok + } + else { throw new VerifyError("Invokespecial cannot call subclass methods"); } @@ -2727,12 +2772,12 @@ sealed class MethodAnalyzer case ClassFile.RefKind.getStatic: case ClassFile.RefKind.putField: case ClassFile.RefKind.putStatic: - err = HardError.NoSuchFieldException; - msg = "no such field: {0}.{1}{2}"; + err = HardError.NoSuchFieldError; + msg = cpi.Name; break; default: - err = HardError.NoSuchMethodException; - msg = "no such method: {0}.{1}{2}"; + err = HardError.NoSuchMethodError; + msg = cpi.Class + "." + cpi.Name + cpi.Signature; break; } SetHardError(wrapper.GetClassLoader(), ref instr, err, msg, cpi.Class, cpi.Name, SigToString(cpi.Signature)); @@ -2745,7 +2790,7 @@ sealed class MethodAnalyzer } else { - SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IllegalAccessException, "member is private: {0}.{1}/{2}, from {3}", cpi.Class, cpi.Name, SigToString(cpi.Signature), wrapper.Name); + SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IllegalAccessException, "member is private: {0}.{1}/{2}/{3}, from {4}", cpi.Class, cpi.Name, SigToString(cpi.Signature), cpi.Kind, wrapper.Name); } } } @@ -3522,9 +3567,7 @@ sealed class MethodAnalyzer msg = Message.EmittedIllegalAccessError; break; case HardError.IncompatibleClassChangeError: - case HardError.NoSuchFieldException: case HardError.IllegalAccessException: - case HardError.NoSuchMethodException: msg = Message.EmittedIncompatibleClassChangeError; break; case HardError.NoSuchFieldError: @@ -3553,25 +3596,30 @@ sealed class MethodAnalyzer NormalizedByteCode invoke = instr.NormalizedOpCode; bool isnew = false; TypeWrapper thisType; - if(invoke == NormalizedByteCode.__invokestatic) - { - thisType = null; - } - else if (invoke == NormalizedByteCode.__invokevirtual - && classFile.MajorVersion >= 51 + if (invoke == NormalizedByteCode.__invokevirtual && cpi.Class == "java.lang.invoke.MethodHandle" - && (cpi.Name == "invoke" || cpi.Name == "invokeExact")) + && (cpi.Name == "invoke" || cpi.Name == "invokeExact" || cpi.Name == "invokeBasic")) { - if (cpi.Name == "invoke") + if (cpi.GetArgTypes().Length > 127 && MethodHandleUtil.SlotCount(cpi.GetArgTypes()) > 254) { - instr.PatchOpCode(NormalizedByteCode.__methodhandle_invoke); - } - else - { - instr.PatchOpCode(NormalizedByteCode.__methodhandle_invokeexact); + instr.SetHardError(HardError.LinkageError, AllocErrorMessage("bad parameter count")); + return; } + instr.PatchOpCode(NormalizedByteCode.__methodhandle_invoke); + return; + } + else if (invoke == NormalizedByteCode.__invokestatic + && cpi.Class == "java.lang.invoke.MethodHandle" + && (cpi.Name == "linkToVirtual" || cpi.Name == "linkToStatic" || cpi.Name == "linkToSpecial" || cpi.Name == "linkToInterface") + && CoreClasses.java.lang.invoke.MethodHandle.Wrapper.IsPackageAccessibleFrom(wrapper)) + { + instr.PatchOpCode(NormalizedByteCode.__methodhandle_link); return; } + else if (invoke == NormalizedByteCode.__invokestatic) + { + thisType = null; + } else { TypeWrapper[] args = cpi.GetArgTypes(); @@ -3625,9 +3673,12 @@ sealed class MethodAnalyzer { SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IncompatibleClassChangeError, "invokeinterface on non-interface"); } - else if(cpi.GetClassType().IsInterface && invoke != NormalizedByteCode.__invokeinterface && (invoke != NormalizedByteCode.__invokestatic || classFile.MajorVersion < 52)) + else if(cpi.GetClassType().IsInterface && invoke != NormalizedByteCode.__invokeinterface && ((invoke != NormalizedByteCode.__invokestatic && invoke != NormalizedByteCode.__invokespecial) || classFile.MajorVersion < 52)) { - SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IncompatibleClassChangeError, "interface method must be invoked used invokeinterface or invokestatic"); + SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IncompatibleClassChangeError, + classFile.MajorVersion < 52 + ? "interface method must be invoked using invokeinterface" + : "interface method must be invoked using invokeinterface, invokespecial or invokestatic"); } else { @@ -3649,6 +3700,24 @@ sealed class MethodAnalyzer { return; } + else if(host != null && targetMethod.IsAccessibleFrom(cpi.GetClassType(), host, thisType)) + { + switch (invoke) + { + case NormalizedByteCode.__invokespecial: + instr.PatchOpCode(NormalizedByteCode.__privileged_invokespecial); + break; + case NormalizedByteCode.__invokestatic: + instr.PatchOpCode(NormalizedByteCode.__privileged_invokestatic); + break; + case NormalizedByteCode.__invokevirtual: + instr.PatchOpCode(NormalizedByteCode.__privileged_invokevirtual); + break; + default: + throw new InvalidOperationException(); + } + return; + } else { // NOTE special case for incorrect invocation of Object.clone(), because this could mean @@ -3662,7 +3731,7 @@ sealed class MethodAnalyzer instr.PatchOpCode(NormalizedByteCode.__clone_array); return; } - SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IllegalAccessError, "Try to access method {0}.{1}{2} from class {3}", targetMethod.DeclaringType.Name, cpi.Name, cpi.Signature, wrapper.Name); + SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IllegalAccessError, "tried to access method {0}.{1}{2} from class {3}", ToSlash(targetMethod.DeclaringType.Name), cpi.Name, ToSlash(cpi.Signature), ToSlash(wrapper.Name)); } } else @@ -3677,6 +3746,11 @@ sealed class MethodAnalyzer } } + private static string ToSlash(string str) + { + return str.Replace('.', '/'); + } + private void PatchFieldAccess(TypeWrapper wrapper, MethodWrapper mw, ref ClassFile.Method.Instruction instr, StackState stack) { ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); |