experiences with prototype

Peter Levart peter.levart at gmail.com
Sun Feb 8 11:28:10 UTC 2015


On 02/08/2015 11:43 AM, Peter Levart wrote:
> I've got it.
>
> The problem was that Any.<int>toStringImpl() instance method was 
> private. Since it is located in a specialized (Any${0=I}) class, it is 
> not accessible from Any class (or specialized VM anonymous class 
> generated for a specialized generic static method). The VerifyError: 
> "Bad invokespecial instruction: current class isn't assignable to 
> reference class." is not very informative though.

Well, it is, if you think about it. "invokespecial" is always used 
internally where it invokes methods of "current" or "super" class. So 
this is not an illegal access error (yet), but a verify error indicating 
that wrong target for "invokespecial" instruction was used. Synthetic 
access/bridge method would be needed here (like with inner classes), but 
as you say, javac de-sugaring phase is executed too late currently.

But couldn't verifier for "invokespecial" be relaxed a bit so it would 
allow calls to private methods of unrelated classes and rather rely on 
access permissions checking at link-time. This and some notion of 
"protection domain" shared by a group of classes, where groups would be 
formed of:
- top-level class + all nested/inner classes
- master class + derived specialized classes

...would also make a secure platform without using synthetic access 
methods. Wouldn't it?

Regards, Peter

>
> By making the method(s) package-private, my test now runs correctly.
>
> Regards, Peter
>
> On 02/08/2015 11:27 AM, Peter Levart wrote:
>> Hi Maurizio,
>>
>> This is great. I replaced all new-style for loops on <any T> T[] 
>> arrays with classic on-index iteration and applied your patch 
>> selectively without the redundant cast in javany/util/Arrays.java:823:
>>
>>     a = (E[])Objects.requireNonNull(array)
>>
>> ... as I think your last langtools patch (99ab651be669) fixes that, 
>> right?
>>
>>
>> Now with the following code:
>>
>> http://cr.openjdk.java.net/~plevart/misc/valhala-hacks/javany-src.02.jar
>>
>> And this test:
>>
>> public class Test {
>>
>>     static void testStrings() {
>>         List<String> strings = Arrays.<String>asList("a", "b", "c");
>>         System.out.println(strings);
>>     }
>>
>>     static void testInts() {
>>         List<int> ints = Arrays.<int>asList(1, 2, 3);
>>         System.out.println(ints);
>>     }
>>
>>     public static void main(String[] args) {
>>         testStrings();
>>         testInts();
>>     }
>> }
>>
>>
>> I get:
>>
>> [a, b, c]
>> Specializing javany.util.List${0=I}; searching for 
>> javany/util/List.class (not found)
>> Specializing javany.util.List${0=I}; searching for 
>> javany/util/List.class (found)
>> Specializing javany.util.Collection${0=I}; searching for 
>> javany/util/Collection.class (not found)
>> Specializing javany.util.Collection${0=I}; searching for 
>> javany/util/Collection.class (found)
>> Specializing javany.lang.Iterable${0=I}; searching for 
>> javany/lang/Iterable.class (not found)
>> Specializing javany.lang.Iterable${0=I}; searching for 
>> javany/lang/Iterable.class (found)
>> Specializing method 
>> javany/util/Arrays$asList${0=I}.asList([Ljava/lang/Object;)Ljavany/util/List; 
>> with class=[] and method=[I]
>> Specializing javany.util.Arrays$ArrayList${0=I}; searching for 
>> javany/util/Arrays$ArrayList.class (not found)
>> Specializing javany.util.Arrays$ArrayList${0=I}; searching for 
>> javany/util/Arrays$ArrayList.class (found)
>> Specializing javany.util.AbstractList${0=I}; searching for 
>> javany/util/AbstractList.class (not found)
>> Specializing javany.util.AbstractList${0=I}; searching for 
>> javany/util/AbstractList.class (found)
>> Specializing javany.util.AbstractCollection${0=I}; searching for 
>> javany/util/AbstractCollection.class (not found)
>> Specializing javany.util.AbstractCollection${0=I}; searching for 
>> javany/util/AbstractCollection.class (found)
>> Specializing javany.util.Iterator${0=I}; searching for 
>> javany/util/Iterator.class (not found)
>> Specializing javany.util.Iterator${0=I}; searching for 
>> javany/util/Iterator.class (found)
>> Specializing javany.util.ListIterator${0=I}; searching for 
>> javany/util/ListIterator.class (not found)
>> Specializing javany.util.ListIterator${0=I}; searching for 
>> javany/util/ListIterator.class (found)
>> Specializing javany.util.AbstractList$Itr${0=I}; searching for 
>> javany/util/AbstractList$Itr.class (not found)
>> Specializing javany.util.AbstractList$Itr${0=I}; searching for 
>> javany/util/AbstractList$Itr.class (found)
>> Specializing method 
>> javany/util/Any$toString${0=I}.toString(Ljava/lang/Object;)Ljava/lang/String; 
>> with class=[] and method=[I]
>> Specializing javany.util.Any${0=I}; searching for 
>> javany/util/Any.class (not found)
>> Specializing javany.util.Any${0=I}; searching for 
>> javany/util/Any.class (found)
>> Exception in thread "main" java.lang.BootstrapMethodError: call site 
>> initialization exception
>>         at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
>>         at 
>> java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
>>         at 
>> java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
>>         at 
>> javany.util.AbstractCollection${0=I}.toString(AbstractCollection.java:514)
>>         at java.lang.String.valueOf(String.java:2987)
>>         at java.io.PrintStream.println(PrintStream.java:821)
>>         at Test.testInts(Test.java:17)
>>         at Test.main(Test.java:22)
>> Caused by: java.lang.VerifyError: Bad invokespecial instruction: 
>> current class isn't assignable to reference class.
>> Exception Details:
>>   Location:
>>     javany/util/Any$toString${0=I}.toString(I)Ljava/lang/String; @8: 
>> invokespecial
>>   Reason:
>>     Error exists in the bytecode
>>   Bytecode:
>>     0000000: bb00 0959 b700 0d1a b700 10b0
>>
>>         at sun.misc.Unsafe.defineAnonymousClass(Native Method)
>>         at 
>> java.lang.invoke.GenericMethodSpecializer.metafactory(GenericMethodSpecializer.java:98)
>>         at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
>>         ... 7 more
>>
>>
>> But looking at generated bytecode:
>>
>>
>> Classfile 
>> /home/peter/work/local/valhalla-test/dump/javany.util.Any$toString${0=I}_ERROR.class
>>   Last modified Feb 8, 2015; size 321 bytes
>>   MD5 checksum 7dac6f4f515394e0aa2ed1990a092697
>>   Compiled from "Any.java"
>> public class javany.util.Any$toString${0=I}
>>   minor version: 0
>>   major version: 52
>>   flags: ACC_PUBLIC, ACC_SUPER
>> Constant pool:
>>    #1 = Utf8               javany/util/Any$toString${0=I}
>>    #2 = Class              #1             // 
>> "javany/util/Any$toString${0=I}"
>>    #3 = Utf8               java/lang/Object
>>    #4 = Class              #3             // java/lang/Object
>>    #5 = Utf8               Any.java
>>    #6 = Utf8               toString
>>    #7 = Utf8               (I)Ljava/lang/String;
>>    #8 = Utf8               javany/util/Any${0=I}
>>    #9 = Class              #8             // "javany/util/Any${0=I}"
>>   #10 = Utf8               <init>
>>   #11 = Utf8               ()V
>>   #12 = NameAndType        #10:#11        // "<init>":()V
>>   #13 = Methodref          #9.#12         // 
>> "javany/util/Any${0=I}"."<init>":()V
>>   #14 = Utf8               toStringImpl
>>   #15 = NameAndType        #14:#7         // 
>> toStringImpl:(I)Ljava/lang/String;
>>   #16 = Methodref          #9.#15         // 
>> "javany/util/Any${0=I}".toStringImpl:(I)Ljava/lang/String;
>>   #17 = Utf8               Code
>>   #18 = Utf8               LineNumberTable
>>   #19 = Utf8               Signature
>>   #20 = Utf8               SourceFile
>> {
>>   public static java.lang.String toString(int);
>>     descriptor: (I)Ljava/lang/String;
>>     flags: ACC_PUBLIC, ACC_STATIC
>>     Code:
>>       stack=2, locals=1, args_size=1
>>          0: new           #9                  // class 
>> "javany/util/Any${0=I}"
>>          3: dup
>>          4: invokespecial #13                 // Method 
>> "javany/util/Any${0=I}"."<init>":()V
>>          7: iload_0
>>          8: invokespecial #16                 // Method 
>> "javany/util/Any${0=I}".toStringImpl:(I)Ljava/lang/String;
>>         11: areturn
>>       LineNumberTable:
>>         line 31: 0
>>     Signature: #7                           // (I)Ljava/lang/String;
>> }
>> SourceFile: "Any.java"
>>
>>
>> Comparing it to original Any.class bytecode:
>>
>>
>>   public static <T extends java.lang.Object> java.lang.String 
>> toString(T);
>>     descriptor: (Ljava/lang/Object;)Ljava/lang/String;
>>     flags: ACC_PUBLIC, ACC_STATIC
>>     Code:
>>       stack=2, locals=1, args_size=1
>>          0: new           #3                  // class javany/util/Any
>>          3: dup
>>          4: invokespecial #4                  // Method "<init>":()V
>>          7: aload_0
>>          8: invokespecial #8                  // Method 
>> toStringImpl:(Ljava/lang/Object;)Ljava/lang/String;
>>         11: areturn
>>       LineNumberTable:
>>         line 31: 0
>> Error: unknown attribute
>>       BytecodeMapping: length = 0x12
>>        00 04 00 00 00 8C 00 04 00 8D 00 07 00 87 00 08
>>        00 9F
>>     Signature: #160                         // 
>> <T:Ljava/lang/Object;>(TT;)Ljava/lang/String;
>> Error: unknown attribute
>>       TypeVariablesMap: length = 0x9
>>        01 00 A1 01 01 00 92 00 86
>>
>>
>> And also peeking at Any${0=I}.class:
>>
>>
>>   private java.lang.String toStringImpl(int);
>>     descriptor: (I)Ljava/lang/String;
>>     flags: ACC_PRIVATE
>>     Code:
>>       stack=2, locals=2, args_size=2
>>          0: aload_0
>>          1: iload_1
>>          2: invokespecial #45                 // Method 
>> toStringNonNullImpl:(I)Ljava/lang/String;
>>          5: areturn
>>       LineNumberTable:
>>         line 181: 0
>>     Signature: #33                          // (I)Ljava/lang/String;
>>
>>
>> ...I can't spot what's wrong. Can you?
>>
>>
>> Regards, Peter
>>
>>
>> On 02/06/2015 03:38 PM, Maurizio Cimadamore wrote:
>>> Peter,
>>> I think I fixed most of the issues in the way; I've been able to 
>>> compile and run some tests successfully.
>>> Attached is a patch of the changes that are required for the code to 
>>> run correctly; I had to desugar some constructs (i.e. for-each, 
>>> non-static inner class) as all desugared stuff currently has an 
>>> issue in that javac won't emit the special sauce that is required by 
>>> the specializer to correctly specialize a class. That's an issue 
>>> with timing of the compilation pipeline - we are aware of it; but, 
>>> as it's a no trivial fix, it means that, for now, it's better not to 
>>> rely too much of compiler-desugared code.
>>>
>>> Maurizio
>>>
>>> On 04/02/15 12:42, Maurizio Cimadamore wrote:
>>>> Compiler fixes have been pushed, I will now look into the runtime 
>>>> issues you are getting...
>>>>
>>>> Maurizio
>>>>
>>>> On 04/02/15 10:18, Maurizio Cimadamore wrote:
>>>>> Thanks for the additional feedback, I'll try to get at the bottom 
>>>>> of those issues.
>>>>>
>>>>> Maurizio
>>>>>
>>>>> On 04/02/15 08:46, Peter Levart wrote:
>>>>>> Hi Maurizio,
>>>>>>
>>>>>> I have now managed to successfully compile the code. Here's the 
>>>>>> updated source:
>>>>>>
>>>>>> http://cr.openjdk.java.net/~plevart/misc/valhala-hacks/javany-src.jar 
>>>>>>
>>>>>>
>>>>>>
>>>>>> But there's a StringIndexOutOfBoundsException thrown from 
>>>>>> specializer when running the following Test:
>>>>>>
>>>>>> public class Test {
>>>>>>     public static void main(String[] args) {
>>>>>>         List<int> ints = Arrays.asList(new int[]{1, 2, 3, 4, 5, 
>>>>>> 6, 7, 8});
>>>>>>         Iterator<int> it = ints.iterator();
>>>>>>         while (it.hasNext()) {
>>>>>>             System.out.println(it.next());
>>>>>>         }
>>>>>>     }
>>>>>> }
>>>>>>
>>>>>>
>>>>>> Specializing javany.util.List${0=I}; searching for 
>>>>>> javany/util/List.class (not found)
>>>>>> Specializing javany.util.List${0=I}; searching for 
>>>>>> javany/util/List.class (found)
>>>>>> Specializing javany.util.Collection${0=I}; searching for 
>>>>>> javany/util/Collection.class (not found)
>>>>>> Specializing javany.util.Collection${0=I}; searching for 
>>>>>> javany/util/Collection.class (found)
>>>>>> Specializing javany.lang.Iterable${0=I}; searching for 
>>>>>> javany/lang/Iterable.class (not found)
>>>>>> Specializing javany.lang.Iterable${0=I}; searching for 
>>>>>> javany/lang/Iterable.class (found)
>>>>>> Specializing method 
>>>>>> javany/util/Arrays$asList${0=I}.asList([Ljava/lang/Object;)Ljavany/util/List; 
>>>>>> with class=[] and method=[I]
>>>>>> Specializing javany.util.Arrays$ArrayList${0=I}; searching for 
>>>>>> javany/util/Arrays$ArrayList.class (not found)
>>>>>> Specializing javany.util.Arrays$ArrayList${0=I}; searching for 
>>>>>> javany/util/Arrays$ArrayList.class (found)
>>>>>> Exception in thread "main" 
>>>>>> java.lang.StringIndexOutOfBoundsException: String index out of 
>>>>>> range: 0
>>>>>>         at java.lang.String.charAt(String.java:646)
>>>>>>         at 
>>>>>> jdk.internal.org.objectweb.asm.signature.SignatureReader.accept(SignatureReader.java:107)
>>>>>>         at 
>>>>>> valhalla.specializer.SignatureSpecializer.forType(SignatureSpecializer.java:72)
>>>>>>         at 
>>>>>> valhalla.specializer.Specializer$ManglingMethodVisitor.visitInvokeDynamicInsn(Specializer.java:679)
>>>>>>         at 
>>>>>> jdk.internal.org.objectweb.asm.ClassReader.readCode(ClassReader.java:1507)
>>>>>>         at 
>>>>>> jdk.internal.org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1084)
>>>>>>         at 
>>>>>> jdk.internal.org.objectweb.asm.ClassReader.accept(ClassReader.java:729) 
>>>>>>
>>>>>>         at 
>>>>>> valhalla.specializer.Specializer.specialize(Specializer.java:79)
>>>>>>         at java.net.URLClassLoader$1.run(URLClassLoader.java:409)
>>>>>>         at java.net.URLClassLoader$1.run(URLClassLoader.java:386)
>>>>>>         at java.security.AccessController.doPrivileged(Native 
>>>>>> Method)
>>>>>>         at 
>>>>>> java.net.URLClassLoader.findClass(URLClassLoader.java:385)
>>>>>>         at java.lang.ClassLoader.loadClass(ClassLoader.java:426)
>>>>>>         at 
>>>>>> sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:317)
>>>>>>         at java.lang.ClassLoader.loadClass(ClassLoader.java:359)
>>>>>>         at 
>>>>>> javany.util.Arrays$asList${0=I}/511754216.asList(Arrays.java:810)
>>>>>>         at Test.main(Test.java:10)
>>>>>>
>>>>>>
>>>>>> Appart from that, I learned that when the component type of 
>>>>>> vararg array is an <any> type variable (for example: <any T> T[] 
>>>>>> Arrays.asList(T ... a)), the invocation doesn't compile:
>>>>>>
>>>>>> src/Test.java:10: error: method invoked with incorrect number of 
>>>>>> arguments; expected 3, found 1
>>>>>>         List<int> ints = Arrays.<int>asList(1, 2, 3);
>>>>>>                          ^
>>>>>> 1 error
>>>>>>
>>>>>>
>>>>>> Non-specialized code also has problems at runtime:
>>>>>>
>>>>>> public class Test {
>>>>>>     public static void main(String[] args) {
>>>>>>         List<String> strings = Arrays.<String>asList("a", "b", "c");
>>>>>>         Iterator<String> it = strings.iterator();
>>>>>>         while (it.hasNext()) {
>>>>>>             System.out.println(it.next());
>>>>>>         }
>>>>>>     }
>>>>>> }
>>>>>>
>>>>>>
>>>>>> Exception in thread "main" java.lang.ClassFormatError: Absent 
>>>>>> Code attribute in method that is not native or abstract in class 
>>>>>> file javany/util/AbstractList
>>>>>>         at java.lang.ClassLoader.defineClass1(Native Method)
>>>>>>         at java.lang.ClassLoader.defineClass(ClassLoader.java:762)
>>>>>>         at 
>>>>>> java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 
>>>>>>
>>>>>>         at 
>>>>>> java.net.URLClassLoader.defineClass(URLClassLoader.java:537)
>>>>>>         at 
>>>>>> java.net.URLClassLoader.access$300(URLClassLoader.java:78)
>>>>>>         at java.net.URLClassLoader$1.run(URLClassLoader.java:438)
>>>>>>         at java.net.URLClassLoader$1.run(URLClassLoader.java:386)
>>>>>>         at java.security.AccessController.doPrivileged(Native 
>>>>>> Method)
>>>>>>         at 
>>>>>> java.net.URLClassLoader.findClass(URLClassLoader.java:385)
>>>>>>         at java.lang.ClassLoader.loadClass(ClassLoader.java:426)
>>>>>>         at 
>>>>>> sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:317)
>>>>>>         at java.lang.ClassLoader.loadClass(ClassLoader.java:359)
>>>>>>         at java.lang.ClassLoader.defineClass1(Native Method)
>>>>>>         at java.lang.ClassLoader.defineClass(ClassLoader.java:762)
>>>>>>         at 
>>>>>> java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 
>>>>>>
>>>>>>         at 
>>>>>> java.net.URLClassLoader.defineClass(URLClassLoader.java:537)
>>>>>>         at 
>>>>>> java.net.URLClassLoader.access$300(URLClassLoader.java:78)
>>>>>>         at java.net.URLClassLoader$1.run(URLClassLoader.java:438)
>>>>>>         at java.net.URLClassLoader$1.run(URLClassLoader.java:386)
>>>>>>         at java.security.AccessController.doPrivileged(Native 
>>>>>> Method)
>>>>>>         at 
>>>>>> java.net.URLClassLoader.findClass(URLClassLoader.java:385)
>>>>>>         at java.lang.ClassLoader.loadClass(ClassLoader.java:426)
>>>>>>         at 
>>>>>> sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:317)
>>>>>>         at java.lang.ClassLoader.loadClass(ClassLoader.java:359)
>>>>>>         at javany.util.Arrays.asList(Arrays.java:810)
>>>>>>         at Test.main(Test.java:10)
>>>>>>
>>>>>>
>>>>>>
>>>>>> Regards, Peter
>>>>>>
>>>>>> On 02/03/2015 10:34 PM, Maurizio Cimadamore wrote:
>>>>>>>
>>>>>>> On 03/02/15 21:05, Peter Levart wrote:
>>>>>>>> Hi Maurizio,
>>>>>>>>
>>>>>>>> I see. I thought this could be a nice idiom for boxing, since 
>>>>>>>> the following:
>>>>>>>>
>>>>>>>> (Object) 42
>>>>>>>>
>>>>>>>> ...is legal and results in an Integer object at runtime.
>>>>>>> I'm not saying this will never work - actually the compiler is 
>>>>>>> currently accepting this kind of idioms, but the specializer 
>>>>>>> does nothing with it, so you'll get runtime errors.
>>>>>>>>
>>>>>>>> But I don't know if a checkcast is actually inserted for 
>>>>>>>> (Object). Could javac redundantly do it in case casting to 
>>>>>>>> Object is from expression of <any> type and also equip 
>>>>>>>> checkcast with BMA indicating the type of expression so that 
>>>>>>>> specialization could replace it with boxing code?
>>>>>>> That will be the way forward, yes
>>>>>>>
>>>>>>> Maurizio
>>>>>>>>
>>>>>>>> Regards, Peter
>>>>>>>>
>>>>>>>>
>>>>>>>> On 02/03/2015 08:09 PM, Maurizio Cimadamore wrote:
>>>>>>>>>
>>>>>>>>> On 03/02/15 18:46, Maurizio Cimadamore wrote:
>>>>>>>>>> I will also investigate on the crash you are getting... 
>>>>>>>>> Hi Peter,
>>>>>>>>> the crash is coming from this code in AbstractCollection (see 
>>>>>>>>> code in bold):
>>>>>>>>>
>>>>>>>>>     public boolean contains(Object o) {
>>>>>>>>>         __WhereVal(E) {
>>>>>>>>>             Iterator<E> it = iterator();
>>>>>>>>>             if (o == null) {
>>>>>>>>>                 return false;
>>>>>>>>>             } else {
>>>>>>>>>                 while (it.hasNext())
>>>>>>>>> *if (o.equals((Object) it.next()))*
>>>>>>>>>                         return true;
>>>>>>>>>             }
>>>>>>>>>             return false;
>>>>>>>>>         }
>>>>>>>>>         __WhereRef(E) {
>>>>>>>>>             Iterator<E> it = iterator();
>>>>>>>>>             if (o == null) {
>>>>>>>>>                 while (it.hasNext())
>>>>>>>>>                     if (it.next() == null)
>>>>>>>>>                         return true;
>>>>>>>>>             } else {
>>>>>>>>>                 while (it.hasNext())
>>>>>>>>>                     if (o.equals(it.next()))
>>>>>>>>>                         return true;
>>>>>>>>>             }
>>>>>>>>>             return false;
>>>>>>>>>         }
>>>>>>>>>     }
>>>>>>>>>
>>>>>>>>> I believe that, apart from the obvious javac bug, the code has 
>>>>>>>>> an issue, as it.next() is supposed to return a value there, 
>>>>>>>>> but you are casting to Object?
>>>>>>>>>
>>>>>>>>> For the records - a simpler test case for the bug is this:
>>>>>>>>>
>>>>>>>>> class Foo<any E> {
>>>>>>>>>     E e;
>>>>>>>>>     E get() { return e; }
>>>>>>>>>
>>>>>>>>>     void test() {
>>>>>>>>>         __WhereVal(E) {
>>>>>>>>>             Object o = (Object)get();
>>>>>>>>>         }
>>>>>>>>>     }
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> Maurizio
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>



More information about the valhalla-dev mailing list