You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Revive productElementName to extract case class field names
This commit adds two methods to the `scala.Product` trait:
```scala
trait Product {
/** Returns the field name of element at index n */
def productElementName(n: Int): String
/** Returns field names of this product. Must have same length as productIterator */
def productElementNames: Iterator[String]
}
```
Both methods have a default implementation which returns the empty
string for all field names.
This commit then changes the code-generation for case classes to
synthesize a `productElementName` method with actual class field names.
The benefit of this change is that it becomes possible to pretty-print
case classes with field names, for example
```scala
case class User(name: String, age: Int)
def toPrettyString(p: Product): String =
p.productElementNames.zip(p.productIterator)
.map { case (name, value) => s"$name=$value" }
.mkString(p.productPrefix + "(", ", ", ")")
toPrettyString(User("Susan", 42))
// res0: String = User(name=Susan, age=42)
```
The downside of this change is that it produces more bytecode for each
case-class definition. Running `:javacp -c` for a case class with three
fields yields the following results
```scala
> case class A(a: Int, b: Int, c: Int)
> :javap -c A
public java.lang.String productElementName(int);
Code:
0: iload_1
1: istore_2
2: iload_2
3: tableswitch { // 0 to 2
0: 28
1: 33
2: 38
default: 43
}
28: ldc 78 // String a
30: goto 58
33: ldc 79 // String b
35: goto 58
38: ldc 80 // String c
40: goto 58
43: new 67 // class java/lang/IndexOutOfBoundsException
46: dup
47: iload_1
48: invokestatic 65 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
51: invokevirtual 70 // Method java/lang/Object.toString:()Ljava/lang/String;
54: invokespecial 73 // Method java/lang/IndexOutOfBoundsException."<init>":(Ljava/lang/String;)V
57: athrow
58: areturn
```
Thanks to Adriaan's help, the estimated cost per `productElementName`
appears to be fixed 56 bytes and then 10 bytes for each field with
the following breakdown:
* 3 bytes for the
[string info](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.3)
(the actual characters are already in the constant pool)
* 4 bytes for the tableswitch entry
* 2 bytes for the ldc to load the string
* 1 byte for areturn
In my opinion, the bytecode cost is acceptably low thanks to the fact
that field name literals are already available in the constant pool.
0 commit comments