Fork me on GitHub
#java
<
2020-12-20
>
andy.fingerhut22:12:49

This is confusing me -- hoping someone knows how to figure out what is happening here. Here is the deftype from Clojure's implementation for the type clojure.core.VecSeq: https://github.com/clojure/clojure/blob/master/src/clj/clojure/gvec.clj#L59-L165

andy.fingerhut22:12:30

I see methods there named iterator, hasheq, equals, hashCode, and many more, but let us focus on those for a moment. When I evaluate a form to try to determine all methods of the class clojure.core.VecSeq, those method names do not appear:

user=> *clojure-version*
{:major 1, :minor 10, :incremental 1, :qualifier nil}
user=> (pprint (map #(.getName %) (seq (.getDeclaredMethods clojure.core.VecSeq))))
("count"
 "next"
 "empty"
 "first"
 "cons"
 "cons"
 "seq"
 "chunkedNext"
 "chunkedFirst"
 "chunkedMore"
 "getBasis"
 "equiv"
 "internal_reduce"
 "more")

andy.fingerhut22:12:44

Part of the Java docs for getDeclaredMethods says "Returns an array containing `Method` objects reflecting all the declared methods of the class or interface represented by this `Class` object, including public, protected, default (package) access, and private methods, but excluding inherited methods.": "https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredMethods--

andy.fingerhut22:12:19

All classes created by deftype a direct subclasses of the java.lang.Object class. But there are quite a few methods in that deftype definition linked above that are methods of interfaces that class VecSeq implements, and I thought those would all show up.

andy.fingerhut22:12:40

I guess the thing really confusing me is: why does getDeclaredMethods return those methods for clojure.core.VecSeq, but not the others that are declared in the deftype?

hiredman23:12:25

I don't know for sure but my guess is Object is the only concrete class deftype allows

hiredman23:12:35

Everything else is an interface

hiredman23:12:52

Ah I see, the question is about why iterator isn't in the list

hiredman23:12:11

I think it relates to your earlier question about bridge methods, my guess is those methods are all specified by more than one of the interfaces and superclass, so there is some special code generation going on, which is marking all of those methods as synthetic

andy.fingerhut23:12:15

There are other classes where getDeclaredMethods explicitly returns several bridged and/or synthetic methods, but I haven't carefully analyzed the conditions under which those occur.

andy.fingerhut23:12:04

This is also difficult to understand -- instances of the class clojure.core.VecSeq seem not to have a method iterator that Clojure can find:

user=> (def pv1 (vector-of :long 1 2 3))
#'user/pv1
user=> (def pvs1 (seq pv1))
#'user/pvs1
user=> (class pv1)
clojure.core.Vec
user=> (class pvs1)
clojure.core.VecSeq
user=> (. pv1 iterator)
#object[clojure.core.Vec$reify__8269 0x4535b6d5 "[email protected]"]
user=> (class (. pv1 iterator))
clojure.core.Vec$reify__8269
user=> (. pvs1 iterator)
Execution error (IllegalArgumentException) at user/eval37 (REPL:1).
No matching field found: iterator for class clojure.core.VecSeq

hiredman23:12:16

that is curious, I just noticed that javap doesn't show any of the missing methods either

andy.fingerhut23:12:34

Hmmm. I may do a binary search on the deftype form of clojure.core.VecSeq to see if it has something weird to do with some error in one of the methods in the middle somewhere, that somehow causes later methods not to be compiled.

hiredman23:12:57

actually those methods are pretty new, let me make sure I am on a new enough clojure version

hiredman23:12:55

ok, there it is in javap

hiredman23:12:00

public java.util.Iterator iterator();
    descriptor: ()Ljava/util/Iterator;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: new           #204                // class clojure/lang/SeqIterator
         3: dup
         4: aload_0
         5: checkcast     #6                  // class clojure/lang/ISeq
         8: invokespecial #207                // Method clojure/lang/SeqIterator."<init>":(Lclojure/lang/ISeq;)V
        11: checkcast     #209                // class java/util/Iterator
        14: areturn
      LineNumberTable:
        line 59: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  this   Lclojure/core/VecSeq;

hiredman23:12:37

Clojure 1.10.2-master-SNAPSHOT
user=> (def pv1 (vector-of :long 1 2 3))
#'user/pv1
user=> (def pvs1 (seq pv1))
#'user/pvs1
user=> (class pv1)
clojure.core.Vec
user=> (class pvs1)
clojure.core.VecSeq
user=> (. pv1 iterator)
#object[clojure.core.Vec$reify__8294 0x75201592 "[email protected]"]
user=> (class (. pv1 iterator))
clojure.core.Vec$reify__8294
user=> (. pvs1 iterator)
#object[clojure.lang.SeqIterator 0x438bad7c "[email protected]"]
user=>

hiredman23:12:17

so the methods are there, they are just very new, so you need a newish clojure build (I built that snapshot locally) to have them

andy.fingerhut23:12:34

Ugh. Yes, I may be comparing latest source code with Clojure 1.10.1 reflection results....

andy.fingerhut23:12:44

yep, that was it. Even more embarrassing since I wrote one of the patches that added one of those methods.