Projects Panama, Valhalla, and Babylon: Java is the New Python v0.9
yanngaelgueheneuc
384 views
172 slides
Mar 06, 2025
Slide 1 of 172
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
About This Presentation
Java has had a tremendous success and, in the last few years, has evolved quite significantly. However, it was still difficult to interface with libraries written in other programming language because of some complexity with JNI and some syntactic and semantic barriers. New projects to improve Java ...
Java has had a tremendous success and, in the last few years, has evolved quite significantly. However, it was still difficult to interface with libraries written in other programming language because of some complexity with JNI and some syntactic and semantic barriers. New projects to improve Java could help alleviate, even nullify, these barriers. Projects Panama, Valhalla, and Babylon exist to make it easier to use different programming and memory models in Java and to interface with foreign programming languages. This presentation describes the problem with the Java “isthmus” and the three projects in details, with real code examples. It shows how, combined, these three projects could make of Java the new Python.
Size: 2.51 MB
Language: en
Added: Mar 06, 2025
Slides: 172 pages
Slide Content
Yann-Gaël Guéhéneuc
(/jan/, he/il)
Work licensed under Creative Commons
BY-NC-SA 4.0 International
Java is the New Python
(Or How to Open the World)
Version 0.9
25/03/05
2/172
Any questions/comments are welcome at [email protected]
Source code available at
https://github.com/ptidejteam/tutorials -
JavaIsTheNewPython
3/172
Inspiration
2025 Is the Last Year of Python Dominance
in AI: Java Comin’
–https://thenewstack.io/2025-is-the-last-year-of-
python-dominance-in-ai-java-comin/
The Isthmus in the VM
–https://cr.openjdk.org/~jrose/panama/isthmus-in -
the-vm-2014.html
4/172
Observations
Machine learning / AI has made some great
progress in recent years
By coincidence, most libraries are written in
C++ or Python
5/172
Observations C++
Complexity of the syntax
Complexity of the semantic
Multiple ways to do the
same things
No garbage collector
No memory safety
Python
Python is slow at runtime
Cannot do mobile apps
Difficult to use with other
languages
High memory consumption
Not used in enterprise
development
Runtime errors
Simplicity
https://www.geeksforgeeks.org/advantages-and-disadvantages-of-cpp/
https://www.geeksforgeeks.org/disadvantages-of-python/
6/172
Observations
“However, enterprises are realizing that Java
is the better choice for enterprise -level
deployments.
We’re likely to see Java
outpace Python within the next 18
months to three years.
”
—Simon Ritter, deputy CTO,
Azul Systems, January 2025
7/172
Observations
“However, enterprises are realizing that Java
is the better choice for enterprise -level
deployments.
We’re likely to see Java
outpace Python within the next 18
months to three years.
”
—Simon Ritter, deputy CTO,
Azul Systems, January 2025
Emphasis mine
8/172
Questions
In Java
–How to best use existing libraries (C++, Python)?
–How to best use “new” hardware (GPUs)?
9/172
Questions
In Java
–How to best open JVM’s closed world?
–How to best bridge memory models?
10/172
https://saf-astronomie.fr/listhme-de-penthievre-et-la- cote-sauvage/
Security
Managed memory
Garbage collection
Unmanaged memory
Unsecure
The Isthmus in the JVM
11/172
IN DETAILS
12/172
The Isthmus in the JVM 1.
Syntax: lambdas vs. function pointers, no C macros, no C++ templates
2.
Naming: naming and scoping
3.
Data types: Booleans, strings, which always have he aders
4.
Storage management: many native libraries operate through pointers
to memory, garbage collection
5.
Exceptions: C++ and Java exceptions behave differently; C APIs
sometimes require ad hoc polling for errors
6.
Semantics: Java strings are immutable while C “stri ngs” are character
arrays, C++ strings are yet different
7.
Performance: strings, boxing, copying cause perform ance “potholes”
8.
Safety: the JVM must continue to operate correctly even in the face of
errors or abuse of any single API
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
13/172
1.
Syntax
Java lambdas
C/C++ function pointers
static void
methodNeedingSomeComparator(
final
BiFunction<Integer, Integer, Integer>
aComparator
) {
System.
out
.println(
aComparator
.apply(1, 2));
System.
out
.println(
aComparator
);
}
...
final
BiFunction<Integer, Integer, Integer>
max4
= Math::max;
Main.methodNeedingSomeComparator(
max4
);
System.
out
.println(
max4
);
typedef int
(*
fp_max_comparator
)(
const int
,
const int
);
void
function_needing_some_comparator(
const
fp_max_comparator
comparator)
{
printf
(
"%d\n"
, compartor(1, 2));
}
int
my_max_function(
const int
a,
const int
b) { ... }
...
function_needing_some_comparator(&my_max_function);
14/172
interface
Executable {
int
myMax(
final int
a
,
final int
b
);
}
class
MyExecutable
implements
Executable {
public int
myMax(
final int
a
,
final int
b
) {
return
Math.max(
a
,
b
);
}
}
final
MyExecutable
max1
=
new
MyExecutable();
System.
out
.println(
max1
.myMax(1, 2));
class
MySecondExecutable
implements
BiFunction<Integer, Integer, Integer> {
public
Integer apply(
final
Integer
a
,
final
Integer
b
) {
return
Math.max(
a
,
b
);
}
}
final
MySecondExecutable
max2
=
new
MySecondExecutable();
Syntax.methodNeedingSomeComparator(
max2
);
final
BiFunction<Integer, Integer, Integer>
max3
=
new
BiFunction<Integer, Integer, Integer>() {
public
Integer apply(
final
Integer
a
,
final
Integer
b
) {
return
Math.max(
a
,
b
);
}
};
Syntax.methodNeedingSomeComparator(
max3
);
final
BiFunction<Integer, Integer, Integer>
max4
= (
final
Integer
a
,
final
Integer
b
) ->
{
return
Math.max(
a
,
b
); };
Syntax.methodNeedingSomeComparator(
max4
);
final
BiFunction<Integer, Integer, Integer>
max5
= Math::max;
Syntax.methodNeedingSomeComparator(
max5
);
15/172
interface
Executable {
int
myMax(
final int
a
,
final int
b
);
}
class
MyExecutable
implements
Executable {
public int
myMax(
final int
a
,
final int
b
) {
return
Math.max(
a
,
b
);
}
}
final
MyExecutable
max1
=
new
MyExecutable();
System.
out
.println(
max1
.myMax(1, 2));
class
MySecondExecutable
implements
BiFunction<Integer, Integer, Integer> {
public
Integer apply(
final
Integer
a
,
final
Integer
b
) {
return
Math.max(
a
,
b
);
}
}
final
MySecondExecutable
max2
=
new
MySecondExecutable();
Syntax.methodNeedingSomeComparator(
max2
);
final
BiFunction<Integer, Integer, Integer>
max3
=
new
BiFunction<Integer, Integer, Integer>() {
public
Integer apply(
final
Integer
a
,
final
Integer
b
) {
return
Math.max(
a
,
b
);
}
};
Syntax.methodNeedingSomeComparator(
max3
);
final
BiFunction<Integer, Integer, Integer>
max4
= (
final
Integer
a
,
final
Integer
b
) ->
{
return
Math.max(
a
,
b
); };
Syntax.methodNeedingSomeComparator(
max4
);
final
BiFunction<Integer, Integer, Integer>
max5
= Math::max;
Syntax.methodNeedingSomeComparator(
max5
);
Can’t be used by the method needing it…
17/172
2.
Naming
Java
Separation
C++
No separation
public class
Naming {
public int
identifier
= 0;
public int
identifier() {
return
0;
}
}
class
Naming
{
public
:
int
identifier
= 0;
void
identifier() {
}
};
error: 'void Naming::identifier()'
conflicts with a previous declaration
18/172
2.
Naming
Java
No nesting
Impact on visibility
C++
Nesting
No impact on visibility
package
a.b;
public class
X {
}
package
b;
public class
X {
}
public static void
main(String[]
args
) {
System.
out
.println(a.b.X.
class
);
System.
out
.println(b.X.
class
);
}
namespace
a {
namespace
b {
int
x = 0;
}
}
namespace
b {
string
x;
}
using namespace
a;
void
foo() {
b::x = 42;
}
error: reference to 'b' is ambiguous
19/172
2.
Naming
Java
Modules
C++
No equivalent
– Maybe linker libs?
module
isthmus.java {
requires
java.logging;
}
package
net.ptidej.isthmus;
import
java.util.logging.Logger;
public class
Main {
private static
Logger
java. Logging
=
Logger.getLogger(
"net.ptidej.isthmus"
);
public static void
main(
final
String[]
args
) {
Main.
logger
.fine(
"Hello, World!"
);
}
}
20/172
3.
Data types
Java
Booleans are their own
Strings are immutable
C/C++
Booleans are shorts
Strings are mutable
bool
a[5];
bool
result;
for
(
int
i = 0; i < 5; i++) {
cout << a[i] <<
" "
;
result += a[i];
}
cout <<
endl
<< result <<
endl
;
boolean
a
[] =
new boolean
[5];
for
(
int
i
= 0;
i
< 5;
i
++) {
System.
out
.println(
a
[
i
]);
}
string
string1 =
"Hello"
;
string
string2 =
", World!"
;
string
string3 = string1 + string2;
cout << string3 <<
endl
;
string3[0] =
'Y'
;
cout << string3 <<
endl
;
final
String
string1
=
"Hello"
;
final
String
string2
=
", World!"
;
final
String
string3
=
string1
+
string2
;
System.
out
.println(
string3
);
final
StringBuilder
string4
=
new
StringBuilder(
string3
);
string4
.setCharAt(0,
'Y'
);
System.
out
.println(
string4
);
21/172
3.
Data types
Java
Performance
Generics
– Autoboxing
C/C++
Performance
Consistency
public interface
IntFunction<R>
public interface
ToIntFunction<T>
vs. Predicate<
int
,
int
>
using namespace
std;
void
abssort(
float
*x,
unsigned
n) {
sort(x, x + n, [](
float
a,
float
b) {
return
(abs(a) < abs(b)); });
}
24/172
5.
Exceptions
Java
NullPointerException
Exceptions are objects and
thrown consistently
C/C++
nullvalues
– One of the purpose of Rust!
Some errors must be
polled actively int
example_errno() { const char
*filename =
"somefile.txt"
;
ifstream
file(filename);
if
(!file) {
switch
(errno) {
case
ENOENT:
cerr <<
"Error: File doesn't exist."
<<
endl
;
break
;
...
default
:
cerr <<
"Error: Cannot open file."
<<
endl
;
break
;
}
return
1;
// Some error happened
}
...
25/172
6.
Semantics
Classes vs. interfaces
Package-only visibility
–Modules
–Sealing
finalvs. const
Single vs. multiple-inheritance
–Virtual vs. non-virtual member functions
…
https://www.cs.gordon.edu/courses/cs212/paperwork/Java-C++comparison.pdf
26/172
7.
Performance
“Code which uses Java primitives performs
on a par corresponding C code, but…”
https://unriskinsight.blogspot.com/2014/06/fa
st-functional-goats-lions-and-wolves.html
https://www.oracle.com/java/technologies/performance-comparisons.html
27/172
7.
Performance
“Code which uses Java primitives performs
on a par corresponding C code, but…”
https://unriskinsight.blogspot.com/2014/06/fa
st-functional-goats-lions-and-wolves.html
Many threats to validity!
https://www.oracle.com/java/technologies/performance-comparisons.html
28/172
8.
Safety
No attacks from untrusted code
No privilege escalation from untrusted code
No crashes
No leaks
No hangs
Rare outages
No unguarded casts
29/172
8.
Safety
No attacks from untrusted code
No privilege escalation from untrusted code
No crashes
No leaks
No hangs
Rare outages
No unguarded casts
See later…
44/172
Java Native Interface
Possible bug in the JVM
– “JVM Segmentation Fault with Single-file Source-code
Program and JNI”
• java Main.classwill throw (correctly)
java.lang.UnsatisfiedLinkError
• java Main.javawill also throw (correctly)
java.lang.UnsatisfiedLinkError
• But delete Main.classand java Main.javaeither throws
(correctly) java.lang.UnsatisfiedLinkErrororwrites
Segmentation faultwithout further info. orfreezes and
must be killed
https://easydrawingguides.com/how-to-draw-a-ladybug-really-easy-drawing-tutorial/
45/172
Java Native Interface
Limitations
–Sits between the two “worlds”
• Some code in Java, some code in C/C++
–Needs marshalling/mapping of data types
–Requires careful idioms
–Could crash the JVM
• Cf. possible Segmentation fault
–Lots of boiler-plate code
46/172
java.lang.foreign.Linker
Focus on memory
Increase amount of Java code
Remove all C boiler-plate code
47/172
java.lang.foreign.Linker
public class
Main {
public static void
main(
final
String[]
args
)
throws
Throwable {
final
Linker
linker
= Linker.nativeLinker();
final
SymbolLookup
dll
= SymbolLookup.libraryLookup(
"net_ptidej_panama_version2_Main.dll"
, Arena.global());
final
MethodHandle
strlen
=
linker
.downcallHandle(
dll
.find(
"stringLength"
).orElseThrow(),
FunctionDescriptor.of(
ValueLayout.
JAVA_LONG
, ValueLayout.
ADDRESS
));
try
(
final
Arena
arena
= Arena.ofConfined()) {
final
MemorySegment
cString
=
arena
.allocateFrom(
"Hello, World!"
);
final long
length
= (
long
)
strlen
.invoke(
cString
);
System.
out
.println(
length
);
}
}
}
#include
<cstring>
#include
"net_ptidej_panama_version2_Main.h"
long
stringLength(
const char
*s) {
return
strlen(s);
}
48/172
java.lang.foreign.Linker
public class
Main {
public static void
main(
final
String[]
args
)
throws
Throwable {
final
Linker
linker
= Linker.nativeLinker();
final
SymbolLookup
dll
= SymbolLookup.libraryLookup(
"net_ptidej_panama_version2_Main.dll"
, Arena.global());
final
MethodHandle
strlen
=
linker
.downcallHandle(
dll
.find(
"stringLength"
).orElseThrow(),
FunctionDescriptor.of(
ValueLayout.
JAVA_LONG
, ValueLayout.
ADDRESS
));
try
(
final
Arena
arena
= Arena.ofConfined()) {
final
MemorySegment
cString
=
arena
.allocateFrom(
"Hello, World!"
);
final long
length
= (
long
)
strlen
.invoke(
cString
);
System.
out
.println(
length
);
}
}
}
#include
<cstring>
#include
"net_ptidej_panama_version2_Main.h"
long
stringLength(
const char
*s) {
return
strlen(s);
}
Much simpler and
“natural” C code
49/172
java.lang.foreign.Linker
Linker
– nativeLinker()
SymbolLookup
– libraryLookup()
MethodHandle
– invoke()
Arena
– global()
– ofConfined()
MemorySegment
– allocateFrom()
ABI (Application Binary Interface)
– Linker for the ABI of the native platform
A symbol is a named entity
– Loads a library, creates a lookup
A typed, executable entity
– Invokes the method handle
Controls the life of native memory
– Program-long, thread-friendly, zeroed
– Temporary, zeroed
A contiguous region of memory
– New memory segment initialised with
the provided, converted value
50/172
java.lang.foreign.Linker
Advantages
–Simpler, natural C code
–More Java code
Limitations
–Must handle manually the foreign library
51/172
java.lang.foreign.Linker
public class
Main {
public static void
main(
final
String[]
args
)
throws
Throwable {
final
Linker
linker
= Linker.nativeLinker();
final
SymbolLookup
stringh
=
linker
.defaultLookup();
final
MethodHandle
strlen
=
linker
.downcallHandle(
stringh
.find(
"strlen"
).orElseThrow(),
FunctionDescriptor.of(ValueLayout.
JAVA_LONG
,
ValueLayout.
ADDRESS
));
try
(
final
Arena
arena
= Arena.ofConfined()) {
final
MemorySegment
cString
=
arena
.allocateFrom(
"Hello, World!"
);
final long
length
= (
long
)
strlen
.invokeExact(
cString
);
System.
out
.println(
length
);
}
}
}
52/172
java.lang.foreign.Linker
public class
Main {
public static void
main(
final
String[]
args
)
throws
Throwable {
final
Linker
linker
= Linker.nativeLinker();
final
SymbolLookup
stringh
=
linker
.defaultLookup();
final
MethodHandle
strlen
=
linker
.downcallHandle(
stringh
.find(
"strlen"
).orElseThrow(),
FunctionDescriptor.of(ValueLayout.
JAVA_LONG
,
ValueLayout.
ADDRESS
));
try
(
final
Arena
arena
= Arena.ofConfined()) {
final
MemorySegment
cString
=
arena
.allocateFrom(
"Hello, World!"
);
final long
length
= (
long
)
strlen
.invokeExact(
cString
);
System.
out
.println(
length
);
}
}
}
Default lookup
53/172
java.lang.foreign.Linker
public class
Main {
public static void
main(
final
String[]
args
)
throws
Throwable {
final
Linker
linker
= Linker.nativeLinker();
final
SymbolLookup
stringh
=
linker
.defaultLookup();
final
MethodHandle
strlen
=
linker
.downcallHandle(
stringh
.find(
"strlen"
).orElseThrow(),
FunctionDescriptor.of(ValueLayout.
JAVA_LONG
,
ValueLayout.
ADDRESS
));
try
(
final
Arena
arena
= Arena.ofConfined()) {
final
MemorySegment
cString
=
arena
.allocateFrom(
"Hello, World!"
);
final long
length
= (
long
)
strlen
.invokeExact(
cString
);
System.
out
.println(
length
);
}
}
}
Default lookup
For a known C function
56/172
Value Classes and Objects
Early access build from Project Valhalla
–https://jdk.java.net/valhalla/
–(Partial) JDK 23 + Valhalla
57/172
Java Split Personalities
58/172
Java Split Personalities
Everything is an Object
59/172
Java Split Personalities
Everything is an Object
Except for primitive types
–Eight of them:
boolean
,
byte
,
char
,
short
,
int
,
long
,
float
,
double
60/172
Java Split Personalities
Everything is an Object
Except for primitive types
–Eight of them:
boolean
,
byte
,
char
,
short
,
int
,
long
,
float
,
double
Except for arrays
–Class of
Object[]
is
[Ljava.lang.Object;
61/172
Java Split Personalities
Everything is an Object
Except for primitive types
–Eight of them:
boolean
,
byte
,
char
,
short
,
int
,
long
,
float
,
double
Except for arrays
–Class of
Object[]
is
[Ljava.lang.Object;
62/172
Performance Costs
These exceptions were a good idea
–In 1995
• Memory fetch ≈ Computation like addition
But circumstances have changed
–In 2025
• Cache miss ≈ 1,000 ×computation like addition
• Significant different relative cost
63/172
Heap allocation
–Memory indirections
–Need flat (cache-efficient) and dense
(memory-efficient) data representation
By-reference parameters
–Pointer dereferencing
–Need flat(ter) calling convention to pass
components by values
Performance Costs
64/172
Development Costs
Generics
–Simpler, consistent APIs
–Primitive types wrinkles
Autoboxing
–Loose identity, gain randomidentity
–Irregularity and cost
–Gotchas
public interface
IntFunction<R>
public interface
ToIntFunction<T>
vs. Predicate<
int
,
int
>
65/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
66/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
NullPointerException
67/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
68/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
Never null (even if missing)
69/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
70/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
Prints
i = 1
d = 2.0
o1 = 1
o2 = 1.0
false
71/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
72/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
Prints
1.
2.
3.
73/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
74/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
The method
f(Object,
boolean)is ambiguous
75/172
(Gotchas) try
{final
Integer
a
=
null
;
System.
out
.println(
"a = "
+
a
);
final int
b
=
a
;
System.
out
.println(
"b = "
+
b
);
}
catch
(
final
NullPointerException
e
) {
}
{
final
ResultSet
rs
=
this
.doSomeDatabaseQuery();
final
Double
value
=
rs
.getDouble(
"someDoubleField"
);
}
{
final
Integer
i
=
new
Integer(1);
final
Double
d
=
new
Double(2.0);
System.
out
.println(
"i = "
+
i
);
System.
out
.println(
"d = "
+
d
);
final
Object
o1
=
i
;
System.
out
.println(
"o1 = "
+
o1
);
final
Object
o2
=
true
?
i
:
d
;
System.
out
.println(
"o2 = "
+
o2
);
System.
out
.println(
o1
.equals(
o2
));
}
{
final int
smallNumberInt
= 42;
if
(
smallNumberInt
== 42)
System.
out
.println(
"1."
);
final
Integer
smallNumberInteger
= Integer.valueOf(
42
);
if
(
smallNumberInteger
== (Object) 42)
System.
out
.println(
"2."
);
final int
largeNumberInt
= 500;
if
(
largeNumberInt
== 500)
System.
out
.println(
"3."
);
final
Integer
largeNumberInteger
= Integer.valueOf(
500
);
if
(
largeNumberInteger
== (Object) 500)
System.
out
.println(
"4."
);
}
{
final int
value
= 42;
final
Vector
v
=
new
Vector();
v
.add(
value
);
System.
out
.println(
"first = "
+
v
.getFirst().getClass());
Main.f(
value
,
false
);
}
private static void
f(
final
Object
a
,
final boolean
b
) {
System.
out
.println(
"f(Object a, boolean b)"
);
}
private static void
f(
final
Object
a
,
final
Object
b
) {
System.
out
.println(
"f(Object a, Object b)"
);
}
76/172
Root Cause
Identity
– All instances (and all
classes) are uniquely
identified
77/172
Root Cause
Identity
– All instances (and all
classes) are uniquely
identified
final int
i1
= 42;
final int
j1
= 42;
System.
out
.println(
i1
==
j1
);
final
Integer
i2
= Integer.valueOf(42);
final
Integer
j2
= Integer.valueOf(
"42"
);
System.
out
.println(
i2
==
j2
);
System.
out
.println(System.identityHashCode(
i2
));
System.
out
.println(System.identityHashCode(
j2
));
final
String
s1
=
"Hello, World!"
;
final
String
s2
=
"Hello, Wordl!"
;
System.
out
.println(
s1
==
s2
);
final
ClassLoader
cl1
= Main.
class
.getClassLoader();
final
Class<?>
c1
=
cl1
.loadClass(
"net.....Main"
);
final
ClassLoader
cl2
= Main.
class
.getClassLoader();
final
Class<?>
c2
=
cl2
.loadClass(
"net.....Main"
);
final
MyClassLoader
cl3
=
new
MyClassLoader();
final
Class<?>
c3
=
cl3
.findClass(
"net.....Main"
);
System.
out
.println(
c1
==
c2
);
System.
out
.println(
c1
==
c3
);
78/172
Root Cause
Identity
– All instances (and all
classes) are uniquely
identified
final int
i1
= 42;
final int
j1
= 42;
System.
out
.println(
i1
==
j1
);
final
Integer
i2
= Integer.valueOf(42);
final
Integer
j2
= Integer.valueOf(
"42"
);
System.
out
.println(
i2
==
j2
);
System.
out
.println(System.identityHashCode(
i2
));
System.
out
.println(System.identityHashCode(
j2
));
final
String
s1
=
"Hello, World!"
;
final
String
s2
=
"Hello, Wordl!"
;
System.
out
.println(
s1
==
s2
);
final
ClassLoader
cl1
= Main.
class
.getClassLoader();
final
Class<?>
c1
=
cl1
.loadClass(
"net.....Main"
);
final
ClassLoader
cl2
= Main.
class
.getClassLoader();
final
Class<?>
c2
=
cl2
.loadClass(
"net.....Main"
);
final
MyClassLoader
cl3
=
new
MyClassLoader();
final
Class<?>
c3
=
cl3
.findClass(
"net.....Main"
);
System.
out
.println(
c1
==
c2
);
System.
out
.println(
c1
==
c3
);
true
true
617901222
617901222
false
true
false
79/172
Solution
Identity-less instances
–Value classes
–Value objects
“Codes like a class, works like an int.”
80/172
Value Classes public value class
Substring
implements
CharSequence {
private
String
str
;
private int
start
;
private int
end
;
public
Substring(
final
String
str
,
final int
start
,
final int
end
) {
Substring.checkBounds(
start
,
end
,
str
.length());
this
.
str
=
str
;
this
.
start
=
start
;
this
.
end
=
end
;
}
public int
length() {
return this
.
end
-
this
.
start
;
}
public char
charAt(
final int
i
) {
Substring.checkBounds(0,
i
, length());
return
str
.charAt(
start
+
i
);
}
public
Substring subSequence(
final int
s
,
final int
e
) {
Substring.checkBounds(
s
,
e
, length());
return new
Substring(
str
,
start
+
s
,
start
+
e
);
}
public
String toString() {
return
str
.substring(
start
,
end
);
}
private static void
checkBounds(
final int
start
,
final int
end
,
final int
length
) {
if
(
start
< 0 ||
end
<
start
||
length
<
end
) {
throw new
IndexOutOfBoundsException();
}
}
}
81/172
Value Classes public value class
Substring
implements
CharSequence {
private
String
str
;
private int
start
;
private int
end
;
public
Substring(
final
String
str
,
final int
start
,
final int
end
) {
Substring.checkBounds(
start
,
end
,
str
.length());
this
.
str
=
str
;
this
.
start
=
start
;
this
.
end
=
end
;
}
public int
length() {
return this
.
end
-
this
.
start
;
}
public char
charAt(
final int
i
) {
Substring.checkBounds(0,
i
, length());
return
str
.charAt(
start
+
i
);
}
public
Substring subSequence(
final int
s
,
final int
e
) {
Substring.checkBounds(
s
,
e
, length());
return new
Substring(
str
,
start
+
s
,
start
+
e
);
}
public
String toString() {
return
str
.substring(
start
,
end
);
}
private static void
checkBounds(
final int
start
,
final int
end
,
final int
length
) {
if
(
start
< 0 ||
end
<
start
||
length
<
end
) {
throw new
IndexOutOfBoundsException();
}
}
}
New keyword
82/172
Value Classes public value class
Substring
implements
CharSequence {
private
String
str
;
private int
start
;
private int
end
;
public
Substring(
final
String
str
,
final int
start
,
final int
end
) {
Substring.checkBounds(
start
,
end
,
str
.length());
this
.
str
=
str
;
this
.
start
=
start
;
this
.
end
=
end
;
}
public int
length() {
return this
.
end
-
this
.
start
;
}
public char
charAt(
final int
i
) {
Substring.checkBounds(0,
i
, length());
return
str
.charAt(
start
+
i
);
}
public
Substring subSequence(
final int
s
,
final int
e
) {
Substring.checkBounds(
s
,
e
, length());
return new
Substring(
str
,
start
+
s
,
start
+
e
);
}
public
String toString() {
return
str
.substring(
start
,
end
);
}
private static void
checkBounds(
final int
start
,
final int
end
,
final int
length
) {
if
(
start
< 0 ||
end
<
start
||
length
<
end
) {
throw new
IndexOutOfBoundsException();
}
}
}
83/172
Value Classes public value class
Substring
implements
CharSequence {
private
String
str
;
private int
start
;
private int
end
;
public
Substring(
final
String
str
,
final int
start
,
final int
end
) {
Substring.checkBounds(
start
,
end
,
str
.length());
this
.
str
=
str
;
this
.
start
=
start
;
this
.
end
=
end
;
}
public int
length() {
return this
.
end
-
this
.
start
;
}
public char
charAt(
final int
i
) {
Substring.checkBounds(0,
i
, length());
return
str
.charAt(
start
+
i
);
}
public
Substring subSequence(
final int
s
,
final int
e
) {
Substring.checkBounds(
s
,
e
, length());
return new
Substring(
str
,
start
+
s
,
start
+
e
);
}
public
String toString() {
return
str
.substring(
start
,
end
);
}
private static void
checkBounds(
final int
start
,
final int
end
,
final int
length
) {
if
(
start
< 0 ||
end
<
start
||
length
<
end
) {
throw new
IndexOutOfBoundsException();
}
}
}
Method call
before super()
84/172
Value Classes public value class
Substring
implements
CharSequence {
private
String
str
;
private int
start
;
private int
end
;
public
Substring(
final
String
str
,
final int
start
,
final int
end
) {
Substring.checkBounds(
start
,
end
,
str
.length());
this
.
str
=
str
;
this
.
start
=
start
;
this
.
end
=
end
;
}
public int
length() {
return this
.
end
-
this
.
start
;
}
public char
charAt(
final int
i
) {
Substring.checkBounds(0,
i
, length());
return
str
.charAt(
start
+
i
);
}
public
Substring subSequence(
final int
s
,
final int
e
) {
Substring.checkBounds(
s
,
e
, length());
return new
Substring(
str
,
start
+
s
,
start
+
e
);
}
public
String toString() {
return
str
.substring(
start
,
end
);
}
private static void
checkBounds(
final int
start
,
final int
end
,
final int
length
) {
if
(
start
< 0 ||
end
<
start
||
length
<
end
) {
throw new
IndexOutOfBoundsException();
}
}
}
85/172
Value Classes
Cannot compile with Eclipse
–Or any other current IDEs?
$ javac --enable-preview -source 23 -Xlint:preview -d ../bin/ net/ptidej/valhalla/valueclass/Substring.java
net\ptidej\valhalla\valueclass\Substring.java:3: warning:
[preview] value classes are a preview feature and may be removed in a future release.
public value class Substring implements CharSequence {
^
net\ptidej\valhalla\valueclass\Substring.java:3: warning:
[preview] value classes are a preview feature and may be removed in a future release.
public value class Substring implements CharSequence {
^
net\ptidej\valhalla\valueclass\Substring.java:8: warning:
[preview] statements before super() is a preview feature and may be removed in a future release.
public Substring(final String str, final int start, final int end) {
^
3 warnings
86/172
Value Classes
The class is implicitly final […]
All instance fields are implicitly final […]
The class does not extend an identityclass
or an identityinterface
All constructors are implicitly regulated, […],
limiting use of this[…]
No instance methods are […] synchronised
(Possibly) The class does not declare a
finalize()method
87/172
Value Classes public static void
main(
final
String[]
args
) {
final
Substring
subs1
=
new
Substring(
"Hello, World!"
, 7, 13);
System.
out
.println(
subs1
);
final
Substring
subs2
=
new
Substring(
"Hello, World!"
, 7, 13);
System.
out
.println(
subs1
);
System.
out
.println(Modifier.isIdentity(Substring.
class
.getModifiers()));
System.
out
.println(
subs1
==
subs2
);
}
88/172
Value Classes
World!
World!
false
true
public static void
main(
final
String[]
args
) {
final
Substring
subs1
=
new
Substring(
"Hello, World!"
, 7, 13);
System.
out
.println(
subs1
);
final
Substring
subs2
=
new
Substring(
"Hello, World!"
, 7, 13);
System.
out
.println(
subs1
);
System.
out
.println(Modifier.isIdentity(Substring.
class
.getModifiers()));
System.
out
.println(
subs1
==
subs2
);
}
89/172
Value and Identity public identity interface
ICounter {
int
getValue();
void
increment();
}
public value interface
IJSONValue {
String toJsonString();
}
90/172
Value and Identity public identity interface
ICounter {
int
getValue();
void
increment();
}
public value interface
IJSONValue {
String toJsonString();
}
$ javac --enable-preview -source 23 -Xlint:preview -d ../bin/ net/ptidej/valhalla/valueclass/ICounter.java
net\ptidej\valhalla\valueclass\ICounter.java:3: error: class, interface, enum, or record expected
public identity interface ICounter {
^
1 error
$ javac --enable-preview -source 23 -Xlint:preview -d ../bin/ net/ptidej/valhalla/valueclass/IJSONValue.java
net\ptidej\valhalla\valueclass\IJSONValue.java:3: warning:
[preview] value classes are a preview feature and may be removed in a future release.
public value interface IJSONValue {
^
net\ptidej\valhalla\valueclass\IJSONValue.java:3: warning:
[preview] value classes are a preview feature and may be removed in a future release.
public value interface IJSONValue {
^
net\ptidej\valhalla\valueclass\IJSONValue.java:3: error: illegal combination of modifiers: value and interface
public value interface IJSONValue {
^
1 error
2 warnings
91/172
Value and Identity public identity interface
ICounter {
int
getValue();
void
increment();
}
public value interface
IJSONValue {
String toJsonString();
}
$ javac --enable-preview -source 23 -Xlint:preview -d ../bin/ net/ptidej/valhalla/valueclass/ICounter.java
net\ptidej\valhalla\valueclass\ICounter.java:3: error: class, interface, enum, or record expected
public identity interface ICounter {
^
1 error $ javac --enable-preview -source 23 -Xlint:preview -d ../bin/ net/ptidej/valhalla/valueclass/IJSONValue.java
net\ptidej\valhalla\valueclass\IJSONValue.java:3: warning:
[preview] value classes are a preview feature and may be removed in a future release.
public value interface IJSONValue {
^
net\ptidej\valhalla\valueclass\IJSONValue.java:3: warning:
[preview] value classes are a preview feature and may be removed in a future release.
public value interface IJSONValue {
^
net\ptidej\valhalla\valueclass\IJSONValue.java:3: error: illegal combination of modifiers: value and interface
public value interface IJSONValue {
^
1 error 2 warnings
92/172
Value and Identity public identity interface
ICounter {
int
getValue();
void
increment();
}
public value interface
IJSONValue {
String toJsonString();
}
$ javac --enable-preview -source 23 -Xlint:preview -d ../bin/ net/ptidej/valhalla/valueclass/ICounter.java
net\ptidej\valhalla\valueclass\ICounter.java:3: error: class, interface, enum, or record expected
public identity interface ICounter {
^
1 error $ javac --enable-preview -source 23 -Xlint:preview -d ../bin/ net/ptidej/valhalla/valueclass/IJSONValue.java
net\ptidej\valhalla\valueclass\IJSONValue.java:3: warning:
[preview] value classes are a preview feature and may be removed in a future release.
public value interface IJSONValue {
^
net\ptidej\valhalla\valueclass\IJSONValue.java:3: warning:
[preview] value classes are a preview feature and may be removed in a future release.
public value interface IJSONValue {
^
net\ptidej\valhalla\valueclass\IJSONValue.java:3: error: illegal combination of modifiers: value and interface
public value interface IJSONValue {
^
1 error 2 warnings
Not yet implemented
93/172
Value and Identity
By default, an interface may be implemented
by both value classes and identity classes
[If an] interface is only meant for one kind of
class or the other
“It is an error for a value class or interface to
extend an identity class or interface […]”
public identity interface
ICounter {
int
getValue();
void
increment();
}
public value interface
IJSONValue {
String toJsonString();
}
94/172
Value and Identity
By default, an interface may be implemented
by both value classes and identity classes
[If an] interface is only meant for one kind of
class or the other
“It is an error for a value class or interface to
extend an identity class or interface […]”
public identity interface
ICounter {
int
getValue();
void
increment();
}
public value interface
IJSONValue {
String toJsonString();
}
New keywords
95/172
Value and Identity
Special considerations
–A functional interface, compatible with lambda
expressions, must allow for both value and
identity implementations
–An abstract class must allow for both value
classes and identity classes by default
–The class Objectis special
• A concrete class
• Not an identity class
• Supports both identity and value subclasses
96/172
Regulated Constructors
Compiler error
–Reading an instance field of the class
–Invoking an instance method of the class
–Using
this
, except in an assignment
–Using
super
to access a field or method
–Instantiating an inner class with
this
as
enclosing instance
–Calling explicitly a non-regulated constructor
(
super()
or
this()
)
97/172
Regulated Constructors
Compiler error
–Reading an instance field of the class
–Invoking an instance method of the class
–Using
this
, except in an assignment
–Using
super
to access a field or method
–Instantiating an inner class with
this
as
enclosing instance
–Calling explicitly a non-regulated constructor
(
super()
or
this()
)
Weaker than the pre-
construction context, itself
weaker than the static context
98/172
Constructor Contexts
Static
– “The purpose of a static context is to demarcate c ode that must not
refer explicitly or implicitly to the current insta nce of the class whose
declaration lexically encloses the static context. for which there is no
current instance defined of the class whose declara tion lexically
encloses the static context.”
Pre-construction
– “Unlike in a static context, an expression in a pr e-construction
context is free, for example, to refer to the type of the instance under
construction.”
Regulated
– “[A] constructor must not make any use of this in its body, except to
write to an instance field.”
https://docs.oracle.com/en/java/javase/22/docs/specs/stateme nts-before-super-jls.html
https://openjdk.org/jeps/8277163
99/172
Constructor Contexts class
A {
}
class
B
extends
A {
private
String
s
;
private int
i
;
static
{
System.
out
.println(
"Loading of B..."
);
}
{
System.
out
.println(
"Building of a B..."
);
this
.
s
=
"Omega"
;
}
B(
final int
a
) {
System.out
.println(
a
);
this
.
s
=
"Alpha"
;
super
();
this
.
i
=
a
;
}
@Override public
String toString() {
return this
.
s
+
" "
+
this
.
i
;
}
}
100/172
Constructor Contexts class
A {
}
class
B
extends
A {
private
String
s
;
private int
i
;
static
{
System.
out
.println(
"Loading of B..."
);
}
{
System.
out
.println(
"Building of a B..."
);
this
.
s
=
"Omega"
;
}
B(
final int
a
) {
System.out
.println(
a
);
this
.
s
=
"Alpha"
;
super
();
this
.
i
=
a
;
}
@Override public
String toString() {
return this
.
s
+
" "
+
this
.
i
;
}
}
Static
101/172
Constructor Contexts class
A {
}
class
B
extends
A {
private
String
s
;
private int
i
;
static
{
System.
out
.println(
"Loading of B..."
);
}
{
System.
out
.println(
"Building of a B..."
);
this
.
s
=
"Omega"
;
}
B(
final int
a
) {
System.out
.println(
a
);
this
.
s
=
"Alpha"
;
super
();
this
.
i
=
a
;
}
@Override public
String toString() {
return this
.
s
+
" "
+
this
.
i
;
}
}
Static Pre-construction
102/172
Constructor Contexts class
A {
}
class
B
extends
A {
private
String
s
;
private int
i
;
static
{
System.
out
.println(
"Loading of B..."
);
}
{
System.
out
.println(
"Building of a B..."
);
this
.
s
=
"Omega"
;
}
B(
final int
a
) {
System.out
.println(
a
);
this
.
s
=
"Alpha"
;
super
();
this
.
i
=
a
;
}
@Override public
String toString() {
return this
.
s
+
" "
+
this
.
i
;
}
}
Static Pre-construction Regulated
103/172
Constructor Contexts class
A {
}
class
B
extends
A {
private
String
s
;
private int
i
;
static
{
System.
out
.println(
"Loading of B..."
);
}
{
System.
out
.println(
"Building of a B..."
);
this
.
s
=
"Omega"
;
}
B(
final int
a
) {
System.out
.println(
a
);
this
.
s
=
"Alpha"
;
super
();
this
.
i
=
a
;
}
@Override public
String toString() {
return this
.
s
+
" "
+
this
.
i
;
}
}
final
B
b
=
new
B(42);
System.
out
.println(
b
);
Static Pre-construction Regulated
104/172
Constructor Contexts class
A {
}
class
B
extends
A {
private
String
s
;
private int
i
;
static
{
System.
out
.println(
"Loading of B..."
);
}
{
System.
out
.println(
"Building of a B..."
);
this
.
s
=
"Omega"
;
}
B(
final int
a
) {
System.out
.println(
a
);
this
.
s
=
"Alpha"
;
super
();
this
.
i
=
a
;
}
@Override public
String toString() {
return this
.
s
+
" "
+
this
.
i
;
}
}
final
B
b
=
new
B(42);
System.
out
.println(
b
);
Loading of B...
42
Building of a B...
Omega 42Static Pre-construction Regulated
105/172
Constructor Contexts
Must enable preview features of Java
– javac --enable-preview -source 23 -cp
../bin/ -d ../bin/
net/ptidej/isthmus/constructor/contexts/*
– java --enable-preview
net.ptidej.isthmus.constructor.contexts.
Main
106/172
Caveats
The ==operator may treat two instances as the same,
where previously they were considered different
Attempts to synchronize on an instance will fail, either a t
compile time or run time
The results of toString(), equals(), and hashCode(), if not
overridden, may change
Assumptions about unique ownership of an instance may
be violated (e.g., an identical instance may be created a t
two different times)
Performance will generally improve, but may have
surprising different characteristics
107/172
Project Babylon
108/172
Thanks to
Paul Sandoz
Juan Fumero
For their code and their help
111/172
Example
f(x, y) = x×(-sin(x×y) + y) ×4
∂f(x, y)/∂x = (-sin(x×y) + y
-x×cos(x×y) ×y) ×4
112/172
Requirements
113/172
Requirements
Model of Java code
114/172
Requirements
Model of Java code
API to manipulate such model
115/172
Requirements
Model of Java code
API to manipulate such model
Interpreter to evaluate such model
116/172
Requirements
Model of Java code
API to manipulate such model
Interpreter to evaluate such model
117/172
Requirements
Model of Java code
API to manipulate such model
Interpreter to evaluate such model
= Code Reflection
118/172
Enabling Code Reflection
As of today
–Don’t even try on Windows
• Even with CygWin…
–Ubuntu 24.04.2 LTS x86_64
> sudo apt-get install autoconf libasound2-dev libcups2-dev libfontconfig1-dev
libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev
> sudo apt-get install sdkman
> sdk install java 23-open
> sdk use java 23-open
> git clone https://github.com/openjdk/babylon.git
> bash configure --with-boot-jdk=${JAVA_HOME}
> make images
> export JAVA_HOME=$ROOT_BABYLON/babylon/build/linux-x86_64-server-release/jdk
> export PATH=$JAVA_HOME/bin:$PATH
https://jjfumero.github.io/posts/2025/02/07/babylon-an d-tornadovm
119/172
Enabling Code Reflection
New javac/javaversions
> java -version
openjdk version "24-internal" 2025-03-18
OpenJDK Runtime Environment (build 24-internal-adhoc.guehenyg.babylon)
OpenJDK 64-bit Server VN (build 24-internal-adhoc.guehenyg.babylon, mixed mode)
120/172
Requirements
Model of Java code
API to manipulate such model
Interpreter to evaluate such model
121/172
Code (Meta-)Model
122/172
Code Model @
CodeReflection
public static double
sub(
final double
a
,
final double
b
) {
return
a
-
b
;
}
final
Method
subMethod
= Main.
class
.getDeclaredMethod(
"sub"
,
double
.
class
,
double
.
class
);
System.out.println(
subMethod
);
final
CoreOp.FuncOp
funcOp
= Op.ofMethod(
subMethod
).get();
System.out.println(
funcOp
.toText());
funcOp
.traverse(
null
, (acc, codeElement) -> {
int
depth = 0;
CodeElement<?, ?> parent = codeElement;
while
((parent = parent.parent()) !=
null
) {
depth++;
}
System.out.println(
" "
.repeat(depth) + codeElement.getClass());
return
acc;
});
123/172
Code Model @
CodeReflection
public static double
sub(
final double
a
,
final double
b
) {
return
a
-
b
;
}
class jdk.incubator.code.op.CoreOp$FuncOp
class jdk.incubator.code.Body
class jdk.incubator.code.Block
class jdk.incubator.code.op.CoreOp$VarOp
class jdk.incubator.code.op.CoreOp$VarOp
class jdk.incubator.code.op.CoreOp$VarAccessOp$VarLoadOp
class jdk.incubator.code.op.CoreOp$VarAccessOp$VarLoadOp
class jdk.incubator.code.op.CoreOp$SubOp
class jdk.incubator.code.op.CoreOp$ReturnOp
124/172
Requirements
Model of Java code
API to manipulate such model
Interpreter to evaluate such model
125/172
API for Code Models
Code models only exist for method
annotated with @CodeReflection
@CodeReflection public static double
sub(
final double
a
,
final double
b
) {
return
a
-
b
;
}
126/172
API for Code Models Without @CodeReflection
Size: 2,633 bytes
With @CodeReflection
Size: 3,437 bytes
public static double sub(double, double);
descriptor: (DD)D
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
0: dload_0
1: dload_2
2: dsub
3: dreturn
LineNumberTable:
line 35: 0
public static double sub(double, double);
descriptor: (DD)D
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
0: dload_0
1: dload_2
2: dsub
3: dreturn
LineNumberTable:
line 35: 0
RuntimeVisibleAnnotations:
0: #109()
jdk.incubator.code.CodeReflection
127/172
API for Code Models Without @CodeReflection
Size: 2,633 bytes
With @CodeReflection
Size: 3,437 bytes
public static double sub(double, double);
descriptor: (DD)D
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
0: dload_0
1: dload_2
2: dsub
3: dreturn
LineNumberTable:
line 35: 0
public static double sub(double, double);
descriptor: (DD)D
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
0: dload_0
1: dload_2
2: dsub
3: dreturn
LineNumberTable:
line 35: 0
RuntimeVisibleAnnotations:
0: #109()
jdk.incubator.code.CodeReflection
131/172
API for Code Models final
Method
subMethod
= Main.
class
.getDeclaredMethod(
"sub"
,
double
.
class
,
double
.
class
);
System.out.println(
subMethod
);
final
CoreOp.FuncOp
funcOp
= Op.ofMethod(
subMethod
).get();
System.out.println(
funcOp
.toText());
funcOp
.traverse(
null
, (acc, codeElement) -> {
int
depth = 0;
CodeElement<?, ?> parent = codeElement;
while
((parent = parent.parent()) !=
null
) {
depth++;
}
System.out.println(
" "
.repeat(depth) + codeElement.getClass());
return
acc;
});
@
CodeReflection
public static double
sub(
final double
a
,
final double
b
) {
return
a
-
b
;
}
132/172
final
Method
subMethod
= Main.
class
.getDeclaredMethod(
"sub"
,
double
.
class
,
double
.
class
);
System.out.println(
subMethod
);
final
CoreOp.FuncOp
funcOp
= Op.ofMethod(
subMethod
).get();
System.out.println(
funcOp
.toText());
funcOp
.traverse(
null
, (acc, codeElement) -> {
int
depth = 0;
CodeElement<?, ?> parent = codeElement;
while
((parent = parent.parent()) !=
null
) {
depth++;
}
System.out.println(
" "
.repeat(depth) + codeElement.getClass());
return
acc;
});
@
CodeReflection
public static double
sub(
final double
a
,
final double
b
) {
return
a
-
b
;
}
public static double net.ptidej.babylon.a.traversal.Main.sub(double,double)
func @"sub" @loc="33:2:[...]/Main.java" (%0 : double, %1 : double)double -> {
%2 : Var<double> = var %0 @"a" @loc="33:2";
%3 : Var<double> = var %1 @"b" @loc="33:2";
%4 : double = var.load %2 @loc="35:10";
%5 : double = var.load %3 @loc="35:14";
%6 : double = sub %4 %5 @loc="35:10";
return %6 @loc="35:3";
};
133/172
Requirements
Model of Java code
API to manipulate such model
Interpreter to evaluate such model
134/172
Interpreter for Code Models package
jdk.incubator.code.interpreter;
public final class
Interpreter {
private
Interpreter() {
}
/**
* Invokes an invokable operation by interpreting the code elements within the
* operations body.
*
<p>
* The sequence of arguments must [consist] of objects corresponding, in order,
* to the invokable operation's
{@link Op.Invokable#parameters() parameters}
. If
* the invokable operation
{@link Op.Invokable#capturedValues() captures values}
* then the sequence of arguments must be appended with objects corresponding,
* in order, to the captured values.
*
*
@param
l the lookup to use for interpreting reflective operations.
*
@param
op the invokeable operation to interpret.
*
@param
args the invokeable's arguments appended with captured arguments, if any.
*
@return
the interpreter result of invokable operation.
*
@param
<T>
the type of Invokable.
*
@throws
InterpreterException if there is a failure to interpret
*
@throws
Throwable if interpretation results in the throwing of an uncaught exception
*/
public static
<T
extends
Op & Op.Invokable> Object invoke(MethodHandles.Lookup
l
, T
op
, Object...
args
) {
// ...
135/172
Interpreter for Code Models
Yes, an equivalent of eval()!
–JavaScript
–Python
In pure Java!
136/172
Interpreter for Code Models final
Method
distanceMethod
= Main.
class
.getDeclaredMethod(
"distance"
,
double
.
class
,
double
.
class
);
final
CoreOp.FuncOp
distanceFuncOp
= Op.ofMethod(
distanceMethod
).get();
final
Double
resultsInterpreted1
= (Double) Interpreter.invoke(
MethodHandles.lookup(),
distanceFuncOp
, 10, 2);
System.out.println(
resultsInterpreted1
);
final
CoreOp.FuncOp
distanceFuncOpSSA
= SSA.transform(
distanceFuncOp
);
final
Double
resultsInterpreted2
= (Double) Interpreter.invoke(
MethodHandles.lookup(),
distanceFuncOpSSA
, 42, 2);
System.out.println(
resultsInterpreted2
);
137/172
Interpreter for Code Models final
Method
distanceMethod
= Main.
class
.getDeclaredMethod(
"distance"
,
double
.
class
,
double
.
class
);
final
CoreOp.FuncOp
distanceFuncOp
= Op.ofMethod(
distanceMethod
).get();
final
Double
resultsInterpreted1
= (Double) Interpreter.invoke(
MethodHandles.lookup(),
distanceFuncOp
, 10, 2);
System.out.println(
resultsInterpreted1
);
final
CoreOp.FuncOp
distanceFuncOpSSA
= SSA.transform(
distanceFuncOp
);
final
Double
resultsInterpreted2
= (Double) Interpreter.invoke(
MethodHandles.lookup(),
distanceFuncOpSSA
, 42, 2);
System.out.println(
resultsInterpreted2
);
8
40
138/172
Interpreter for Code Models
https://www.pinterest.com/pin/697213586035124567/
139/172
Interpreter for Code Models final
Method
subMethod
= Main.
class
.getDeclaredMethod(
"sub"
,
double
.
class
,
double
.
class
);
final
CoreOp.FuncOp
funcOp1
= Op.ofMethod(
subMethod
).get();
final
Double
resultsInterpreted1
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp1
, 32, 10);
System.out.println(
resultsInterpreted1
);
final
CoreOp.FuncOp
funcOp2
=
funcOp1
.transform((builder, op) -> {
final
CopyContext cc = builder.context();
if
(op
instanceof
CoreOp.SubOp subOp) {
final
Op.Result inputResult = subOp.result();
final
Op.Result lhs = (Op.Result) cc.getProperty(
"beforeLast"
);
final
Op.Result rhs = (Op.Result) cc.getProperty(
"last"
);
final
Op.Result outputResult = builder.op(CoreOp.add(lhs, rhs));
cc.mapValue(inputResult, outputResult);
}
else if
(op
instanceof
CoreOp.VarAccessOp.VarLoadOp varLoadOp) {
final
Op.Result result = builder.op(varLoadOp);
cc.putProperty(
"beforeLast"
, cc.getProperty(
"last"
));
cc.putProperty(
"last"
, result);
}
else
{
builder.op(op);
}
return
builder;
});
final
Double
resultsInterpreted2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp2
, 32, 10);
System.out.println(
resultsInterpreted2
);
140/172
Interpreter for Code Models final
Method
subMethod
= Main.
class
.getDeclaredMethod(
"sub"
,
double
.
class
,
double
.
class
);
final
CoreOp.FuncOp
funcOp1
= Op.ofMethod(
subMethod
).get();
final
Double
resultsInterpreted1
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp1
, 32, 10);
System.out.println(
resultsInterpreted1
);
final
CoreOp.FuncOp
funcOp2
=
funcOp1
.transform((builder, op) -> {
final
CopyContext cc = builder.context();
if
(op
instanceof
CoreOp.SubOp subOp) {
final
Op.Result inputResult = subOp.result();
final
Op.Result lhs = (Op.Result) cc.getProperty(
"beforeLast"
);
final
Op.Result rhs = (Op.Result) cc.getProperty(
"last"
);
final
Op.Result outputResult = builder.op(CoreOp.add(lhs, rhs));
cc.mapValue(inputResult, outputResult);
}
else if
(op
instanceof
CoreOp.VarAccessOp.VarLoadOp varLoadOp) {
final
Op.Result result = builder.op(varLoadOp);
cc.putProperty(
"beforeLast"
, cc.getProperty(
"last"
));
cc.putProperty(
"last"
, result);
}
else
{
builder.op(op);
}
return
builder;
});
final
Double
resultsInterpreted2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp2
, 32, 10);
System.out.println(
resultsInterpreted2
);
141/172
Interpreter for Code Models final
Method
subMethod
= Main.
class
.getDeclaredMethod(
"sub"
,
double
.
class
,
double
.
class
);
final
CoreOp.FuncOp
funcOp1
= Op.ofMethod(
subMethod
).get();
final
Double
resultsInterpreted1
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp1
, 32, 10);
System.out.println(
resultsInterpreted1
);
final
CoreOp.FuncOp
funcOp2
=
funcOp1
.transform((builder, op) -> {
final
CopyContext cc = builder.context();
if
(op
instanceof
CoreOp.SubOp subOp) {
final
Op.Result inputResult = subOp.result();
final
Op.Result lhs = (Op.Result) cc.getProperty(
"beforeLast"
);
final
Op.Result rhs = (Op.Result) cc.getProperty(
"last"
);
final
Op.Result outputResult = builder.op(CoreOp.add(lhs, rhs));
cc.mapValue(inputResult, outputResult);
}
else if
(op
instanceof
CoreOp.VarAccessOp.VarLoadOp varLoadOp) {
final
Op.Result result = builder.op(varLoadOp);
cc.putProperty(
"beforeLast"
, cc.getProperty(
"last"
));
cc.putProperty(
"last"
, result);
}
else
{
builder.op(op);
}
return
builder;
});
final
Double
resultsInterpreted2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp2
, 32, 10);
System.out.println(
resultsInterpreted2
);
22
142/172
Interpreter for Code Models final
Method
subMethod
= Main.
class
.getDeclaredMethod(
"sub"
,
double
.
class
,
double
.
class
);
final
CoreOp.FuncOp
funcOp1
= Op.ofMethod(
subMethod
).get();
final
Double
resultsInterpreted1
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp1
, 32, 10);
System.out.println(
resultsInterpreted1
);
final
CoreOp.FuncOp
funcOp2
=
funcOp1
.transform((builder, op) -> {
final
CopyContext cc = builder.context();
if
(op
instanceof
CoreOp.SubOp subOp) {
final
Op.Result inputResult = subOp.result();
final
Op.Result lhs = (Op.Result) cc.getProperty(
"beforeLast"
);
final
Op.Result rhs = (Op.Result) cc.getProperty(
"last"
);
final
Op.Result outputResult = builder.op(CoreOp.add(lhs, rhs));
cc.mapValue(inputResult, outputResult);
}
else if
(op
instanceof
CoreOp.VarAccessOp.VarLoadOp varLoadOp) {
final
Op.Result result = builder.op(varLoadOp);
cc.putProperty(
"beforeLast"
, cc.getProperty(
"last"
));
cc.putProperty(
"last"
, result);
}
else
{
builder.op(op);
}
return
builder;
});
final
Double
resultsInterpreted2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp2
, 32, 10);
System.out.println(
resultsInterpreted2
);
22
143/172
Interpreter for Code Models final
Method
subMethod
= Main.
class
.getDeclaredMethod(
"sub"
,
double
.
class
,
double
.
class
);
final
CoreOp.FuncOp
funcOp1
= Op.ofMethod(
subMethod
).get();
final
Double
resultsInterpreted1
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp1
, 32, 10);
System.out.println(
resultsInterpreted1
);
final
CoreOp.FuncOp
funcOp2
=
funcOp1
.transform((builder, op) -> {
final
CopyContext cc = builder.context();
if
(op
instanceof
CoreOp.SubOp subOp) {
final
Op.Result inputResult = subOp.result();
final
Op.Result lhs = (Op.Result) cc.getProperty(
"beforeLast"
);
final
Op.Result rhs = (Op.Result) cc.getProperty(
"last"
);
final
Op.Result outputResult = builder.op(CoreOp.add(lhs, rhs));
cc.mapValue(inputResult, outputResult);
}
else if
(op
instanceof
CoreOp.VarAccessOp.VarLoadOp varLoadOp) {
final
Op.Result result = builder.op(varLoadOp);
cc.putProperty(
"beforeLast"
, cc.getProperty(
"last"
));
cc.putProperty(
"last"
, result);
}
else
{
builder.op(op);
}
return
builder;
});
final
Double
resultsInterpreted2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp2
, 32, 10);
System.out.println(
resultsInterpreted2
);
22 42
144/172
Example
f(x, y) = x×(-sin(x×y) + y) ×4
∂f(x, y)/∂x = (-sin(x×y) + y
-x×cos(x×y) ×y) ×4
145/172
Example
Origin
@
CodeReflection
static double
f(
final double
x
,
final double
y
) {
return
x
* (-Math.sin(
x
*
y
) +
y
) * 4.0d;
}
static double
df_dx(
double
x
,
double
y
) {
return
(-Math.sin(
x
*
y
) +
y
-
x
* Math.cos(
x
*
y
) *
y
) * 4.0d;
}
146/172
Example
Transformation
final
Method
f
= Main.
class
.getDeclaredMethod(
"f"
,
double
.
class
,
double
.
class
);
final
Double
result_f1
= f(2, 3);
System.out.println(
result_f1
);
final
CoreOp.FuncOp
funcOp_f
= Op.ofMethod(
f
).get();
final
CoreOp.FuncOp
funcOp_f_SSA
= SSA.transform(
funcOp_f
);
final
Double
result_f2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_f_SSA
, 2, 3);
System.out.println(
result_f2
);
final
Double
result_df_dx1
= df_dx(2, 3);
System.out.println(
result_df_dx1
);
final
Block.Parameter
x
=
funcOp_f_SSA
.body().entryBlock().parameters().get(0);
final
CoreOp.FuncOp
funcOp_df_dx
= ExpressionElimination.eliminate(ForwardDifferentiation.partialDiff(
funcOp_f_SSA
,
x
));
final
Double
result_df_dx2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_df_dx
, 2, 3);
System.out.println(
result_df_dx2
);
147/172
Example
Transformation
final
Method
f
= Main.
class
.getDeclaredMethod(
"f"
,
double
.
class
,
double
.
class
);
final
Double
result_f1
= f(2, 3);
System.out.println(
result_f1
);
final
CoreOp.FuncOp
funcOp_f
= Op.ofMethod(
f
).get();
final
CoreOp.FuncOp
funcOp_f_SSA
= SSA.transform(
funcOp_f
);
final
Double
result_f2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_f_SSA
, 2, 3);
System.out.println(
result_f2
);
final
Double
result_df_dx1
= df_dx(2, 3);
System.out.println(
result_df_dx1
);
final
Block.Parameter
x
=
funcOp_f_SSA
.body().entryBlock().parameters().get(0);
final
CoreOp.FuncOp
funcOp_df_dx
=
ExpressionElimination.eliminate
(
ForwardDifferentiation.partialDiff
(
funcOp_f_SSA
,
x
));
final
Double
result_df_dx2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_df_dx
, 2, 3);
System.out.println(
result_df_dx2
);
148/172
Example
Transformation
final
Method
f
= Main.
class
.getDeclaredMethod(
"f"
,
double
.
class
,
double
.
class
);
final
Double
result_f1
= f(2, 3);
System.out.println(
result_f1
);
final
CoreOp.FuncOp
funcOp_f
= Op.ofMethod(
f
).get();
final
CoreOp.FuncOp
funcOp_f_SSA
= SSA.transform(
funcOp_f
);
final
Double
result_f2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_f_SSA
, 2, 3);
System.out.println(
result_f2
);
final
Double
result_df_dx1
= df_dx(2, 3);
System.out.println(
result_df_dx1
);
final
Block.Parameter
x
=
funcOp_f_SSA
.body().entryBlock().parameters().get(0);
final
CoreOp.FuncOp
funcOp_df_dx
=
ExpressionElimination.eliminate
(
ForwardDifferentiation.partialDiff
(
funcOp_f_SSA
,
x
));
final
Double
result_df_dx2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_df_dx
, 2, 3);
System.out.println(
result_df_dx2
);
The differentiation happens here
149/172
final
Method
f
= Main.
class
.getDeclaredMethod(
"f"
,
double
.
class
,
double
.
class
);
final
Double
result_f1
= f(2, 3);
System.out.println(
result_f1
);
final
CoreOp.FuncOp
funcOp_f
= Op.ofMethod(
f
).get();
final
CoreOp.FuncOp
funcOp_f_SSA
= SSA.transform(
funcOp_f
);
final
Double
result_f2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_f_SSA
, 2, 3);
System.out.println(
result_f2
);
final
Double
result_df_dx1
= df_dx(2, 3);
System.out.println(
result_df_dx1
);
final
Block.Parameter
x
=
funcOp_f_SSA
.body().entryBlock().parameters().get(0);
final
CoreOp.FuncOp
funcOp_df_dx
= ExpressionElimination.eliminate(ForwardDifferentiation.partialDiff(
funcOp_f_SSA
,
x
));
final
Double
result_df_dx2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_df_dx
, 2, 3);
System.out.println(
result_df_dx2
);
Example
Results
– f(2, 3) = 2×(-sin(2×3) + 3) ×4
= 26.235323985591407
–∂f(2, 3)/∂x = (-sin(2×3) + 3 -2×cos(2×3) ×3) ×4
= -9.92642488681308
150/172
final
Method
f
= Main.
class
.getDeclaredMethod(
"f"
,
double
.
class
,
double
.
class
);
final
Double
result_f1
= f(2, 3);
System.out.println(
result_f1
);
final
CoreOp.FuncOp
funcOp_f
= Op.ofMethod(
f
).get();
final
CoreOp.FuncOp
funcOp_f_SSA
= SSA.transform(
funcOp_f
);
final
Double
result_f2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_f_SSA
, 2, 3);
System.out.println(
result_f2
);
final
Double
result_df_dx1
= df_dx(2, 3);
System.out.println(
result_df_dx1
);
final
Block.Parameter
x
=
funcOp_f_SSA
.body().entryBlock().parameters().get(0);
final
CoreOp.FuncOp
funcOp_df_dx
= ExpressionElimination.eliminate(ForwardDifferentiation.partialDiff(
funcOp_f_SSA
,
x
));
final
Double
result_df_dx2
= (Double) Interpreter.invoke(MethodHandles.lookup(),
funcOp_df_dx
, 2, 3);
System.out.println(
result_df_dx2
);
Example
Results
– f(2, 3) = 2×(-sin(2×3) + 3) ×4
= 26.235323985591407
–∂f(2, 3)/∂x = (-sin(2×3) + 3 -2×cos(2×3) ×3) ×4
= -9.92642488681308
26.235323985591407
26.235323985591407
-9.92642488681308
-9.92642488681308
151/172
Other Example
Emulating C# LINQ in Java
–Language INtegrated Query
152/172
DISCUSSIONS
153/172
Questions
In Java
–How to best open JVM’s closed world?
–How to best bridge memory models?
154/172
Answers
In Java
–How to best open JVM’s closed world?
• Panama
• Babylon
–How to best bridge memory models?
• Panama
• Valhalla
155/172
The Isthmus in the JVM 1.
Syntax: lambdas vs. function pointers, no C macros, no C++ templates
2.
Naming: naming and scoping
3.
Data types: Booleans, strings, which always have he aders
4.
Storage management: many native libraries operate through pointers
to memory, garbage collection
5.
Exceptions: C++ and Java exceptions behave differently; C APIs
sometimes require ad hoc polling for errors
6.
Semantics: Java strings are immutable while C “stri ngs” are character
arrays, C++ strings are yet different
7.
Performance: strings, boxing, copying cause perform ance “potholes”
8.
Safety: the JVM must continue to operate correctly even in the face of
errors or abuse of any single API
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
156/172
The Isthmus in the JVM 1.
Syntax: lambdas vs. function pointers, no C
macros, no C++ templates
With project Panama, possible to manipulate
C pointers
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
157/172
The Isthmus in the JVM 2.
Naming: naming and scoping
Nothing much here…
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
158/172
The Isthmus in the JVM 3.
Data types: Booleans, strings, which always
have headers
With Project Valhalla, possible to create
value classes/objects
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
159/172
The Isthmus in the JVM 4.
Storage management: many native libraries
operate through pointers to memory,
garbage collection
With Project Panama, possible to manipulate
memory from Java
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
160/172
The Isthmus in the JVM 5.
Exceptions: C++ and Java exceptions
behave differently; C APIs sometimes
require ad hoc polling for errors
With Project Babylon, possible to rewrite
code automagically
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
161/172
The Isthmus in the JVM 6.
Semantics: Java strings are immutable
while C “strings” are character arrays, C++
strings are yet different
With Project Babylon, possible to rewrite
code automagically
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
162/172
The Isthmus in the JVM 7.
Performance: strings, boxing, copying
cause performance “potholes”
With Project Valhalla, possible to have
“primitive” performance
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
163/172
The Isthmus in the JVM 8.
Safety: the JVM must continue to operate
correctly even in the face of errors or abuse
of any single API
With Project Panama, more Java code, less
C code, less risks
https://cr.openjdk.org/~jrose/panama/isthmus-in-the-vm-2 014.html
166/172
With Such An Approach final
OpenAiChatModel
model
= OpenAiChatModel.builder().apiKey(Main.
API_KEY
)
.modelName(OpenAiChatModelName.
GPT_4_O_MINI
).build();
final
String
answer
=
model
.chat(
"Give me the advantages and disadvantages of Java"
);
System.
out
.println(
answer
);
167/172
With Such An Approach final
OpenAiChatModel
model
= OpenAiChatModel.builder().apiKey(Main.
API_KEY
)
.modelName(OpenAiChatModelName.
GPT_4_O_MINI
).build();
final
String
answer
=
model
.chat(
"Give me the advantages and disadvantages of Java"
);
System.
out
.println(
answer
);
Java is a widely-used programming language known for its versatility, portability, and robust nature. Here are some
advantages and disadvantages of using Java:
### Advantages of Java
1. **Platform Independence**: Java is designed to be platform-independent at both the source and binary levels, thanks
to the Java Virtual Machine (JVM). This allows developers to write code once and run it anywhere (Write Once, Run
Anywhere - WORA).
[...]
8. **Mature Ecosystem**: Java has a mature ecosystem with enterprise-level solutions, making it a preferred choice for
large-scale applications, including web and mobile development.
### Disadvantages of Java
1. **Performance**: Java can be slower than some compiled languages (such as C or C++) due to the overhead of the JVM
and garbage collection, making it less suitable for performance-critical applications.
[...]
8. **Dependence on JVM**: Java applications require the Java Runtime Environment (JRE) to run, which may not be
available in all environments.
In conclusion, Java is a powerful and versatile language with many advantages, particularly in enterprise and cross-
platform applications. However, developers should consider its disadvantages when selecting a programming language for
specific projects.
168/172
With Such An Approach
Actually…
https://github.com/langchain4j/langchain4j/blob/main/l angchain4j-open-
ai/src/main/java/dev/langchain4j/model/openai/OpenAiChatModel.java
this
.client = OpenAiClient.builder()
.httpClientBuilder(builder.httpClientBuilder)
.baseUrl(getOrDefault(builder.baseUrl, DEFAULT_OPENAI_URL))
.apiKey(builder.apiKey)
.organizationId(builder.organizationId)
.projectId(builder.projectId)
.connectTimeout(getOrDefault(builder.timeout, ofSeconds(15)))
.readTimeout(getOrDefault(builder.timeout, ofSeconds(60)))
.logRequests(getOrDefault(builder.logRequests,
false
))
.logResponses(getOrDefault(builder.logResponses,
false
))
.userAgent(DEFAULT_USER_AGENT)
.customHeaders(builder.customHeaders)
.build();
169/172
With Such An Approach
Actually…
https://github.com/langchain4j/langchain4j/blob/main/l angchain4j-open-
ai/src/main/java/dev/langchain4j/model/openai/OpenAiChatModel.java
this
.client = OpenAiClient.builder()
.httpClientBuilder(builder.httpClientBuilder)
.baseUrl(getOrDefault(builder.baseUrl, DEFAULT_OPENAI_URL))
.apiKey(builder.apiKey)
.organizationId(builder.organizationId)
.projectId(builder.projectId)
.connectTimeout(getOrDefault(builder.timeout, ofSeconds(15)))
.readTimeout(getOrDefault(builder.timeout, ofSeconds(60)))
.logRequests(getOrDefault(builder.logRequests,
false
))
.logResponses(getOrDefault(builder.logResponses,
false
))
.userAgent(DEFAULT_USER_AGENT)
.customHeaders(builder.customHeaders)
.build();
It’s a REST call!
170/172
With Such An Approach
Three ways for language interoperability
–REST APIs and such
–Script engines
–FFIs
They have different (dis)advantages!
171/172
With Such An Approach
REST APIs and such
– Simple
– Slow, duplications
Script engines
– Complex
– Fast, some duplications
FFIs
– Convoluted
– Fast, no duplications
172/172
With Such An Approach
REST APIs and such
– Simple
– Slow, duplications
Script engines
– Complex
– Fast, some duplications
FFIs
–Convoluted
–Very fast
, no duplications
With Projects Panama,
Valhalla, and Babylon!