Skip to content

When overrided function return type subtypes parent type in Java, generated Java bytecode is wrong #7955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
molikto opened this issue Jan 10, 2020 · 4 comments

Comments

@molikto
Copy link
Contributor

molikto commented Jan 10, 2020

When trying to make this Scala.js test works in Dotty https://github.com/scala-js/scala-js/blob/master/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/ReadersTest.scala#L63

Scala.js linker fails with

      [error] Referring to non-existent method java.nio.CharBuffer.slice()java.nio.Buffer

I found that the IR generated is different

in Dotty:

    val buf: java.nio.CharBuffer = buf0.slice;Ljava.nio.Buffer().asInstanceOf[java.nio.CharBuffer];

in Scala 2.13:

    val buf: java.nio.CharBuffer = buf0.slice;Ljava.nio.CharBuffer();

minimized code

This is minimized code:

import java.nio.CharBuffer

class test {

val a: CharBuffer = null

val b = a.slice()	
}

The class file seems have wrong type in it's constant pool, and will have a checkcast instruction; see bytecode bellow:

Compilation output
Classfile /Users/molikto/GitHub/dotty/test.class
  Last modified Jan 10, 2020; size 600 bytes
  MD5 checksum 25414eae76502c40f16327ad77826708
  Compiled from "test.scala"
public class test
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2                          // test
  super_class: #4                         // java/lang/Object
  interfaces: 0, fields: 2, methods: 3, attributes: 3
Constant pool:
   #1 = Utf8               test
   #2 = Class              #1             // test
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               test.scala
   #6 = Utf8               a
   #7 = Utf8               Ljava/nio/CharBuffer;
   #8 = Utf8               b
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = NameAndType        #9:#10         // "<init>":()V
  #12 = Methodref          #4.#11         // java/lang/Object."<init>":()V
  #13 = NameAndType        #6:#7          // a:Ljava/nio/CharBuffer;
  #14 = Fieldref           #2.#13         // test.a:Ljava/nio/CharBuffer;
  #15 = Utf8               ()Ljava/nio/CharBuffer;
  #16 = NameAndType        #6:#15         // a:()Ljava/nio/CharBuffer;
  #17 = Methodref          #2.#16         // test.a:()Ljava/nio/CharBuffer;
  #18 = Utf8               java/nio/CharBuffer
  #19 = Class              #18            // java/nio/CharBuffer
  #20 = Utf8               slice
  #21 = Utf8               ()Ljava/nio/Buffer;
  #22 = NameAndType        #20:#21        // slice:()Ljava/nio/Buffer;
  #23 = Methodref          #19.#22        // java/nio/CharBuffer.slice:()Ljava/nio/Buffer;
  #24 = NameAndType        #8:#7          // b:Ljava/nio/CharBuffer;
  #25 = Fieldref           #2.#24         // test.b:Ljava/nio/CharBuffer;
  #26 = Utf8               this
  #27 = Utf8               Ltest;
  #28 = Utf8               Code
  #29 = Utf8               LineNumberTable
  #30 = Utf8               LocalVariableTable
  #31 = Utf8               SourceFile
  #32 = Utf8               TASTY
  #33 = Utf8               Scala
{
  public test();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #12                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: aconst_null
         6: putfield      #14                 // Field a:Ljava/nio/CharBuffer;
         9: aload_0
        10: aload_0
        11: invokevirtual #17                 // Method a:()Ljava/nio/CharBuffer;
        14: invokevirtual #23                 // Method java/nio/CharBuffer.slice:()Ljava/nio/Buffer;
        17: checkcast     #19                 // class java/nio/CharBuffer
        20: putfield      #25                 // Field b:Ljava/nio/CharBuffer;
        23: return
      LineNumberTable:
        line 3: 0
        line 5: 4
        line 7: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      24     0  this   Ltest;

  public java.nio.CharBuffer a();
    descriptor: ()Ljava/nio/CharBuffer;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #14                 // Field a:Ljava/nio/CharBuffer;
         4: areturn
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ltest;

  public java.nio.CharBuffer b();
    descriptor: ()Ljava/nio/CharBuffer;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #25                 // Field b:Ljava/nio/CharBuffer;
         4: areturn
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ltest;
}
SourceFile: "test.scala"
Error: unknown attribute
  TASTY: length = 0x10
   00 B5 CF 56 F8 F6 DC 00 00 05 3A 7B D6 3C 8F 00

Error: unknown attribute
  Scala: length = 0x0

This is Tasty file (seems no cast is present here):

Compilation output
     0: PACKAGE(78)
     2:   TERMREFpkg 1 [<empty>]
     4:   IMPORT(6)
     6:     SELECT 2 [nio]
     8:       TERMREFpkg 3 [java]
    10:     IMPORTED 4 [CharBuffer]
    12:   TYPEDEF(66) 5 [test]
    15:     TEMPLATE(48)
    17:       APPLY(7)
    19:         SELECT 11 [<init>[Signed Signature(List(),java.lang.Object)]]
    21:           NEW
    22:             TYPEREF 9 [Object]
    24:               TERMREFpkg 8 [java[Qualified . lang]]
    26:       DEFDEF(6) 6 [<init>]
    29:         PARAMEND
    30:         TYPEREF 12 [Unit]
    32:           TERMREFpkg 13 [scala]
    34:       VALDEF(8) 14 [a]
    37:         IDENTtpt 4 [CharBuffer]
    39:           TYPEREF 4 [CharBuffer]
    41:             TERMREFpkg 15 [java[Qualified . nio]]
    43:         NULLconst
    44:       VALDEF(19) 16 [b]
    47:         TYPEREF 4 [CharBuffer]
    49:           TERMREFpkg 15 [java[Qualified . nio]]
    51:         APPLY(12)
    53:           SELECT 19 [slice[Signed Signature(List(),java.nio.CharBuffer)]]
    55:             SELECT 14 [a]
    57:               QUALTHIS
    58:                 IDENTtpt 5 [test]
    60:                   TYPEREFsymbol 12
    62:                     THIS
    63:                       TYPEREFpkg 1 [<empty>]
    65:     ANNOTATION(13)
    67:       TYPEREF 20 [SourceFile]
    69:         TERMREFpkg 24 [scala[Qualified . annotation][Qualified . internal]]
    71:       APPLY(7)
    73:         SELECT 28 [<init>[Signed Signature(List(java.lang.String),scala.annotation.internal.SourceFile)]]
    75:           NEW
    76:             SHAREDtype 67
    78:         STRINGconst 29 [test.scala]
    80:

It doesn't seems to happen with similar Scala code:


abstract class Buffer {
	def slice(): Buffer
}

abstract class CharBuffer extends Buffer {
	override def slice(): CharBuffer
}

class test {

val a: CharBuffer = null

val b = a.slice()	
}
@molikto molikto changed the title .asInstanceOf is unnecessarily generated When overrided function return type subtypes parent type in Java, generated Java bytecode is wrong Jan 10, 2020
@smarter
Copy link
Member

smarter commented Jan 10, 2020

The CharBuffer example can only be reproduced in Java 9 or superior as previous versions did not have a slice() method in the base class Buffer. This might or might not be related to #6546, /cc @anatoliykmetyuk

@molikto
Copy link
Contributor Author

molikto commented Jan 13, 2020

Tested that it is indeed the same issue. Can I take a shot at this? @smarter

@molikto molikto closed this as completed Jan 13, 2020
@smarter
Copy link
Member

smarter commented Jan 13, 2020

Sure, but how is it the same issue ? I don't see anything package private in CharBuffer

@molikto
Copy link
Contributor Author

molikto commented Jan 14, 2020

what I means is the issue of bridge method is read from classfile reader. I want to try to make visibility bridge generation works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants