coerce a number to a short

somewhat-functional-programmer somewhat-functional-programmer at protonmail.com
Fri Dec 13 19:14:13 UTC 2019


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 at panix.com> wrote:

> > On Nov 27, 2019, at 05:53, somewhat-functional-programmer somewhat-functional-programmer at 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]
>
> [ABCL-PROVE]: https://github.com/armedbear/abcl/pull/131
>
> ------------------------------------------------------------------------------------------------------
>
> "A screaming comes across the sky. It has happened before but there is nothing
> to compare to it now."


-------------- next part --------------
A non-text attachment was scrubbed...
Name: primitive-narrowing-jcoerce-fix.patch
Type: text/x-patch
Size: 11693 bytes
Desc: not available
URL: <https://mailman.common-lisp.net/pipermail/armedbear-devel/attachments/20191213/3fd00a2c/attachment.bin>


More information about the armedbear-devel mailing list