I've looked into this issue at length now and I think I have something worth discussing and potentially trying[0] . The root issue here lies in how ABCL converts its CL representation of integers to Java, as well as its conversion of Java primitives to other Java primitives.
ABCL's common superclass for all objects (LispObject) includes two methods to convert the lisp object to a Java representation: LispObject.javaInstance() and LispObject.javaInstance(Class). LispObject.javaInstance(Class) presumably tries to convert the internal representation to the type represented by the Class passed in. In practice, this seems to be primarily used for Java primitive types. The current implementation of these methods in ABCL's internal Bignum, DoubleFloat, SingleFloat, Fixnum, LispCharacter, and JavaObject, do not seem to have all the required permutations necessary to support all of the possible Java primitive converting operations.
Even for instances where a conversion /is/ implemented (such as Fixnum.javaInstance(Short) where a Short would be returned), the method lookup strategy in Java.findMethod (which boils down to what Java.isAssignable does) doesn't find the method with the Short parameter. Instead it uses normal rules in the Java Language spec and only looks for methods that potentially /widen/ primitives, and disallows any narrowing without an explicit narrowing cast (and in Brad's example, the literal 16 or 32 is represented by Fixnum, which converts to java.lang.Integer). I think the original intention is to have such methods be called using jcoerce, such as:
(jcoerce 32 (jclass "short")) or (jcoerce 32 (jclass "java.lang.Short"))
This fails in the current implementation, I believe due to a bug in JavaObject(Object, Class) constructor. This only does one type of conversion, to java.lang.Byte, if the intended class is Byte and the Object implements Number.
For jcoerce to work with all primitives, all possible primitive operations need to be covered in JavaObject(Object, Class).
I have attached a patch that adds these conversions to JavaObject(Object, Class). However, I've also changed other places where these primitive conversions are done for consistency. My rule of thumb has been that the Java Language Specification rules[1] should be followed, and the /only/ time a /narrowing/ conversion is allowed where information can potentially be lost, is through the JavaObject(Object, Class) constructor (in Lisp land, called using the (java:jcoerce) function).
As such, the patch also changes the implementation classes Bignum, DoubleFloat, SingleFloat, Fixnum, Character, and JavaObject, to support all possible primitive /widening/ conversions. Their implementations of LispObject.javaInstance(Class) continue to signal type errors for conversions that cannot be performend, but also now for all narrowing operations. Previously /some/ of these classes allowed /some/ narrowing conversions to happen. I think a case could be made for each of these LispObject implementations to allow any primitve conversion. The disadvantage is not knowing an information reducing operation potentially occurred (and is counter to the behavior of not finding narrowing methods such as Brad's original example) -- at the very least if all primitive narrowing operations /were/ allowed from Fixnum.javaInstance(Class) for example, a compiler warning should be emitted (though I'm not sure how that would be implemented).
The following patch may break code that relies on the one conversion that has worked in the past (to byte, in the JavaObject(Object, Class) constructor. In fact, this patch breaks swank. To fix your swank, modify your swank's abcl.lisp to include the now required jcoerce call (in the octets-to-jbytes function): the jstatic call should now look like: ... (around line 191) do (java:jstatic (java:jmethod "java.lang.reflect.Array" "setByte" "java.lang.Object" "int" "byte") "java.lang.reflect.Array" bytes i (java:jcoerce byte "byte")))
What are your thoughts? I dislike backwards incompatible changes like this, but I think the impact is probably low due to the fact that method resolution used rules to only find widening conversions, and the fact that short and byte are not as commonly used in method calls.
Previously, (jfield) and (jproperty) uses would also not signal a type error unless LispObject.javaInstance(Class) did not support the conversion (and like mentioned previously, some classes did some primitive narrowing operations). Uses of these fields will now (consistently) with method calls, error on narrowing conversions unless coerced.
Try the patch (but make sure to modify your swank!)...
Now my old example works like the following: (jstatic "reverseBytes" "java.lang.Short" (jcoerce #x7f "short"))
Note also that the patch didn't break any new tests. I ran tests via: (require 'asdf) (load "~/quicklisp/setup.lisp") (asdf:initialize-source-registry `(:source-registry (:directory ,*default-pathname-defaults*) :inherit-configuration)) (asdf:test-system :abcl)
-Mark
[0] See patch attached (against latest master of https://github.com/armedbear/abcl) (3a54c91f3a0cbb71ae8d161acadecf118a65404c)
[1] Widening Primitve Conversion https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ On Wednesday, December 4, 2019 8:37 PM, Mark Evenson evenson@panix.com wrote:
On Nov 27, 2019, at 05:53, somewhat-functional-programmer somewhat-functional-programmer@protonmail.com wrote: Here's a workaround in the meantime (though it's more verbose)... using java.lang.Short.reverseBytes(short s) as the test case[1]: ;; no such method failure (jstatic "reverseBytes" "java.lang.Short" #x7f)
[Tests via ABCL-PROVE added.][ABCL-PROVE]
"A screaming comes across the sky. It has happened before but there is nothing to compare to it now."