Skip to content

Commit 853522b

Browse files
author
Elia Trachsel
committed
[GR-70147] EspressoNoNative: Synchronize SocketOptions between host and guest
The guest encodes SocketOptions to ints in the platform-specific sun.nio.ch.SocketOptionRegistry.findOption. Those encodings are then provided to nativ get/set methods.  In the substitutions of those methods in EspressoNoNative we have to decode the ints back to the original SocketOption.  To ensure consistency between guest SocketOptions and host SocketOptions across platforms, we substitute findOption to use our custom encoding.
1 parent 9063f15 commit 853522b

6 files changed

Lines changed: 162 additions & 67 deletions

File tree

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,12 @@ public static void ensureInitialized() {
183183
public static final Symbol<Type> java_net_InetAddress_array = SYMBOLS.putType("[Ljava/net/InetAddress;");
184184
public static final Symbol<Type> sun_net_ConnectionResetException = SYMBOLS.putType("Lsun/net/ConnectionResetException;");
185185
public static final Symbol<Type> java_net_UnknownHostException = SYMBOLS.putType("Ljava/net/UnknownHostException;");
186+
public static final Symbol<Type> java_net_SocketOption = SYMBOLS.putType("Ljava/net/SocketOption;");
186187
public static final Symbol<Type> sun_nio_ch_IOStatus = SYMBOLS.putType("Lsun/nio/ch/IOStatus;");
187188
public static final Symbol<Type> java_net_spi_InetAddressResolver$LookupPolicy = SYMBOLS.putType("Ljava/net/spi/InetAddressResolver$LookupPolicy;");
188189
public static final Symbol<Type> sun_nio_ch_Net = SYMBOLS.putType("Lsun/nio/ch/Net;");
190+
public static final Symbol<Type> sun_nio_ch_OptionKey = SYMBOLS.putType("Lsun/nio/ch/OptionKey;");
191+
189192
// libjava
190193
public static final Symbol<Type> java_lang_ProcessHandleImpl$Info = SYMBOLS.putType("Ljava/lang/ProcessHandleImpl$Info;");
191194
// libnio
@@ -1260,6 +1263,7 @@ public static class Signatures {
12601263
public static final Symbol<Signature> _void_Exception = SYMBOLS.putSignature(Types._void, Types.java_lang_Exception);
12611264
public static final Symbol<Signature> _void_String_String_String_int = SYMBOLS.putSignature(Types._void, Types.java_lang_String, Types.java_lang_String, Types.java_lang_String, Types._int);
12621265
public static final Symbol<Signature> _void_int = SYMBOLS.putSignature(Types._void, Types._int);
1266+
public static final Symbol<Signature> _void_int_int = SYMBOLS.putSignature(Types._void, Types._int, Types._int);
12631267
public static final Symbol<Signature> _void_boolean = SYMBOLS.putSignature(Types._void, Types._boolean);
12641268
public static final Symbol<Signature> _void_long = SYMBOLS.putSignature(Types._void, Types._long);
12651269
public static final Symbol<Signature> _void_long_int = SYMBOLS.putSignature(Types._void, Types._long, Types._int);

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ public final class LibNetMeta {
185185
public final ObjectKlass java_net_InetSocketAddress;
186186
public final Method java_net_InetSocketAddress_init;
187187

188+
// Synchronize guest and host SocketOptions.
189+
public final ObjectKlass sun_nio_ch_OptionKey;
190+
public final Method sun_nio_ch_OptionKey_init;
191+
public final ObjectKlass java_net_SocketOption;
192+
public final Method java_net_SocketOption_name;
193+
188194
// Checkstyle: resume field name check
189195

190196
private LibNetMeta() {
@@ -221,6 +227,11 @@ private LibNetMeta() {
221227

222228
java_net_InetSocketAddress = knownKlass(EspressoSymbols.Types.java_net_InetSocketAddress);
223229
java_net_InetSocketAddress_init = java_net_InetSocketAddress.lookupDeclaredMethod(EspressoSymbols.Names._init_, EspressoSymbols.Signatures.java_net_InetSocketAddress_init_signature);
230+
231+
sun_nio_ch_OptionKey = knownKlass(EspressoSymbols.Types.sun_nio_ch_OptionKey);
232+
sun_nio_ch_OptionKey_init = sun_nio_ch_OptionKey.requireDeclaredMethod(EspressoSymbols.Names._init_, EspressoSymbols.Signatures._void_int_int);
233+
java_net_SocketOption = knownKlass(EspressoSymbols.Types.java_net_SocketOption);
234+
java_net_SocketOption_name = java_net_SocketOption.requireDeclaredMethod(EspressoSymbols.Names.name, EspressoSymbols.Signatures.String);
224235
}
225236
}
226237

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@
2929
import java.net.Inet6Address;
3030
import java.net.InetAddress;
3131
import java.net.NetworkInterface;
32+
import java.net.SocketOption;
33+
import java.net.StandardSocketOptions;
3234
import java.nio.channels.CancelledKeyException;
3335
import java.nio.channels.SelectableChannel;
3436
import java.nio.channels.SelectionKey;
3537
import java.nio.channels.Selector;
3638
import java.util.Arrays;
39+
import java.util.HashMap;
3740
import java.util.Iterator;
41+
import java.util.Map;
3842
import java.util.Map.Entry;
3943
import java.util.Objects;
4044
import java.util.concurrent.ConcurrentHashMap;
@@ -484,7 +488,7 @@ public void pollerCleanSelectionKey(int fdToClean) {
484488

485489
/**
486490
* Only used for testing.
487-
*
491+
*
488492
* @return true if there are no registrations for the TrufflePoller
489493
*/
490494
public boolean noPollerRegisterations() {
@@ -637,4 +641,66 @@ private InetAddress getInetAddress(String hostName, int address, boolean preferI
637641
return guestInetAddr;
638642
}
639643
}
644+
645+
/**
646+
* Class for synchronizing platform-specific socket options between the host and the guest.
647+
*/
648+
public static final class SocketOptionSync {
649+
/*
650+
* The guest encodes SocketOptions to ints in the platform-specific
651+
* sun.nio.ch.SocketOptionRegistry.findOption. Those encodings are then provided to native
652+
* get/set methods. In the substitutions of those methods in EspressoNoNative we have to
653+
* decode the ints back to the original SocketOption. To ensure consistency between guest
654+
* SocketOptions and host SocketOptions across platforms, we substitute findOption to use our
655+
* custom encoding defined by this class.
656+
*/
657+
658+
// Defines all supported StandardSocketOptions. ExtendedSocketOptions are currently disabled GR-78554.
659+
private static final SocketOption<?>[] intToOption = {
660+
StandardSocketOptions.SO_BROADCAST,
661+
StandardSocketOptions.SO_KEEPALIVE,
662+
StandardSocketOptions.SO_SNDBUF,
663+
StandardSocketOptions.SO_RCVBUF,
664+
StandardSocketOptions.SO_REUSEADDR,
665+
StandardSocketOptions.SO_REUSEPORT,
666+
StandardSocketOptions.SO_LINGER,
667+
StandardSocketOptions.IP_TOS,
668+
StandardSocketOptions.TCP_NODELAY,
669+
StandardSocketOptions.IP_MULTICAST_IF,
670+
StandardSocketOptions.IP_MULTICAST_TTL,
671+
StandardSocketOptions.IP_MULTICAST_LOOP
672+
};
673+
private static final Map<String, Integer> optionNameToInt = createOptionNameToInt();
674+
675+
private static Map<String, Integer> createOptionNameToInt() {
676+
Map<String, Integer> result = new HashMap<>(intToOption.length);
677+
for (int i = 0; i < intToOption.length; i++) {
678+
String name = intToOption[i].name();
679+
if (result.put(name, i) != null) {
680+
throw new IllegalStateException("Duplicate SocketOption name: " + name);
681+
}
682+
}
683+
return result;
684+
}
685+
686+
/**
687+
* Retrieve the int encoding associated with the given name of the SocketOption.
688+
*
689+
* @param name the name associated with the SocketOption
690+
* @return the encoding or -1 if the option was not found
691+
*/
692+
public static int getInt(String name) {
693+
return name == null ? -1 : optionNameToInt.getOrDefault(name, -1);
694+
}
695+
696+
/**
697+
* Retrieve the SocketOption associated with the given encoding.
698+
*
699+
* @param code the encoded SocketOption
700+
* @return the SocketOption
701+
*/
702+
public static SocketOption<?> getOption(int code) {
703+
return intToOption[code];
704+
}
705+
}
640706
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Target_jdk_net_ExtendedSocketOptions_PlatformSocketOptions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public final class Target_jdk_net_ExtendedSocketOptions_PlatformSocketOptions {
3434
@Substitution(languageFilter = EspressoLibsFilter.class)
3535
public static @JavaType(internalName = "Ljdk/net/ExtendedSocketOptions$PlatformSocketOptions;") StaticObject create(
3636
@Inject LibsMeta libsMeta) {
37+
// todo (GR-78554)
3738
// lazily initialize jdk/net
3839
libsMeta.initJdkNet();
3940
// create a nonPlatformSpecificOptions instance to return

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java

Lines changed: 6 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.net.InetSocketAddress;
3333
import java.net.SocketAddress;
3434
import java.net.SocketOption;
35-
import java.net.StandardSocketOptions;
3635
import java.nio.channels.SelectionKey;
3736
import java.nio.channels.Selector;
3837

@@ -260,14 +259,13 @@ public static int localPort(@JavaType(FileDescriptor.class) StaticObject fd, @In
260259
@SuppressWarnings("unchecked")
261260
@TruffleBoundary
262261
public static void setIntOption0(@JavaType(FileDescriptor.class) StaticObject fd, @SuppressWarnings("unused") boolean mayNeedConversion,
263-
int level, int opt, int arg, @SuppressWarnings("unused") boolean isIPv6,
264-
@Inject EspressoContext ctx, @Inject TruffleIO io, @Inject LibsState libsState) {
262+
int level, @SuppressWarnings("unused") int opt, int arg, @SuppressWarnings("unused") boolean isIPv6, @Inject TruffleIO io, @Inject LibsState libsState) {
265263
libsState.net.checkNetworkEnabled();
266264
// We set the option over the public NetworkChannel API, thus the low-level platform
267265
// specific arguments like mayNeedConversion and isIpv6 aren't needed
268266

269267
// recover SocketOption and do Type-Conversion
270-
SocketOption<?> socketOption = getSocketOption(level, opt, ctx);
268+
SocketOption<?> socketOption = getSocketOption(level);
271269
Class<?> type = socketOption.type();
272270
if (type == Integer.class) {
273271
SocketOption<Integer> intSocketOption = (SocketOption<Integer>) socketOption;
@@ -286,14 +284,13 @@ public static void setIntOption0(@JavaType(FileDescriptor.class) StaticObject fd
286284
@SuppressWarnings("unchecked")
287285
@TruffleBoundary
288286
public static int getIntOption0(@JavaType(FileDescriptor.class) StaticObject fd, @SuppressWarnings("unused") boolean mayNeedConversion,
289-
int level, int opt,
290-
@Inject EspressoContext ctx, @Inject TruffleIO io, @Inject LibsState libsState) {
287+
int level, @SuppressWarnings("unused") int opt, @Inject TruffleIO io, @Inject LibsState libsState) {
291288
libsState.net.checkNetworkEnabled();
292289
// We get the option over the public NetworkChannel API, thus the low-level platform
293290
// mayNeedConversion and isIpv6 aren't needed
294291

295292
// recover SocketOption and do Type-Conversion
296-
SocketOption<?> socketOption = getSocketOption(level, opt, ctx);
293+
SocketOption<?> socketOption = getSocketOption(level);
297294
Class<?> type = socketOption.type();
298295
if (type == Integer.class) {
299296
SocketOption<Integer> intSocketOption = (SocketOption<Integer>) socketOption;
@@ -378,64 +375,7 @@ private static int nativeToSelectorOps(int nativeOps, EspressoContext ctx) {
378375
return ops;
379376
}
380377

381-
private static SocketOption<?> getSocketOption(int level, int opt, EspressoContext ctx) {
382-
// todo (GR-70147): synchronize between host and guest
383-
// be aware of GR-71965
384-
switch (level) {
385-
case 0:
386-
switch (opt) {
387-
case 1:
388-
return StandardSocketOptions.IP_TOS;
389-
case 32:
390-
return StandardSocketOptions.IP_MULTICAST_IF;
391-
case 33:
392-
return StandardSocketOptions.IP_MULTICAST_TTL;
393-
case 34:
394-
return StandardSocketOptions.IP_MULTICAST_LOOP;
395-
}
396-
break;
397-
case 1:
398-
switch (opt) {
399-
case 2:
400-
return StandardSocketOptions.SO_REUSEADDR;
401-
case 6:
402-
return StandardSocketOptions.SO_BROADCAST;
403-
case 7:
404-
return StandardSocketOptions.SO_SNDBUF;
405-
case 8:
406-
return StandardSocketOptions.SO_RCVBUF;
407-
case 9:
408-
return StandardSocketOptions.SO_KEEPALIVE;
409-
case 10:
410-
/*
411-
* Would be a ExtendedSocketOption.SO_OOBINLINE, however ExtendedOption are
412-
* package private so we cannot easily access them. For set and getOption of
413-
* Net, extended options aren't accessed over the native world anyway thus
414-
* we shouldn't reach here
415-
*/
416-
throw JavaSubstitution.unimplemented();
417-
case 13:
418-
return StandardSocketOptions.SO_LINGER;
419-
case 15:
420-
return StandardSocketOptions.SO_REUSEPORT;
421-
}
422-
break;
423-
case 41:
424-
switch (opt) {
425-
case 17:
426-
return StandardSocketOptions.IP_MULTICAST_IF;
427-
case 18:
428-
return StandardSocketOptions.IP_MULTICAST_TTL;
429-
case 19:
430-
return StandardSocketOptions.IP_MULTICAST_LOOP;
431-
case 67:
432-
return StandardSocketOptions.IP_TOS;
433-
}
434-
break;
435-
}
436-
if (level == 6 && opt == 1) {
437-
return StandardSocketOptions.TCP_NODELAY;
438-
}
439-
throw Throw.throwUnsupported("Unsupported SocketOption: level = " + level + ", opt = " + opt, ctx);
378+
private static SocketOption<?> getSocketOption(int level) {
379+
return LibsState.SocketOptionSync.getOption(level);
440380
}
441381
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package com.oracle.truffle.espresso.libs.libnio.impl;
24+
25+
import java.net.ProtocolFamily;
26+
import java.net.SocketOption;
27+
28+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
29+
import com.oracle.truffle.espresso.libs.EspressoLibsFilter;
30+
import com.oracle.truffle.espresso.libs.LibsMeta;
31+
import com.oracle.truffle.espresso.libs.LibsState;
32+
import com.oracle.truffle.espresso.meta.Meta;
33+
import com.oracle.truffle.espresso.runtime.EspressoContext;
34+
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
35+
import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions;
36+
import com.oracle.truffle.espresso.substitutions.Inject;
37+
import com.oracle.truffle.espresso.substitutions.JavaSubstitution;
38+
import com.oracle.truffle.espresso.substitutions.JavaType;
39+
import com.oracle.truffle.espresso.substitutions.Substitution;
40+
41+
@EspressoSubstitutions
42+
public final class Target_sun_nio_ch_SocketOptionRegistry {
43+
/*
44+
* As we just use the int level to encode SocketOptions we define here a dummy value as the
45+
* (unused) name of the OptionKey.
46+
*/
47+
private static final int OPTION_KEY_NAME = 0;
48+
49+
@TruffleBoundary
50+
@Substitution(languageFilter = EspressoLibsFilter.class)
51+
public static @JavaType(internalName = "Lsun/nio/ch/OptionKey;") StaticObject findOption(@JavaType(SocketOption.class) StaticObject name,
52+
@SuppressWarnings("unused") @JavaType(ProtocolFamily.class) StaticObject family,
53+
@Inject LibsMeta libsMeta,
54+
@Inject Meta meta,
55+
@Inject EspressoContext context) {
56+
// First retrieve the name of the guest options.
57+
Object result = libsMeta.net.java_net_SocketOption_name.invokeDirectInterface(name);
58+
// Use the name to get the int encoding.
59+
String optionName = meta.toHostString((StaticObject) result);
60+
int level = LibsState.SocketOptionSync.getInt(optionName);
61+
if (level == -1) {
62+
throw JavaSubstitution.shouldNotReachHere();
63+
}
64+
return makeGuestOptionKey(level, libsMeta, context);
65+
}
66+
67+
private static @JavaType(internalName = "Lsun/nio/ch/OptionKey;") StaticObject makeGuestOptionKey(int level, LibsMeta libsMeta, EspressoContext context) {
68+
StaticObject guestObject = libsMeta.net.sun_nio_ch_OptionKey.allocateInstance(context);
69+
libsMeta.net.sun_nio_ch_OptionKey_init.invokeDirectSpecial(guestObject, level, OPTION_KEY_NAME);
70+
return guestObject;
71+
}
72+
73+
}

0 commit comments

Comments
 (0)