Skip to content

Commit 0e927fa

Browse files
committed
[refactor] call-site-ize SSLSocket as #initialize gets hit a lot
1 parent d61f1dc commit 0e927fa

File tree

2 files changed

+80
-42
lines changed

2 files changed

+80
-42
lines changed

src/main/java/org/jruby/ext/openssl/SSLSocket.java

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -48,26 +48,15 @@
4848
import javax.net.ssl.SSLHandshakeException;
4949
import javax.net.ssl.SSLPeerUnverifiedException;
5050

51-
import org.jruby.Ruby;
52-
import org.jruby.RubyArray;
53-
import org.jruby.RubyClass;
54-
import org.jruby.RubyHash;
55-
import org.jruby.RubyIO;
56-
import org.jruby.RubyModule;
57-
import org.jruby.RubyNumeric;
58-
import org.jruby.RubyObject;
59-
import org.jruby.RubyString;
60-
import org.jruby.RubyThread;
51+
import org.jruby.*;
6152
import org.jruby.anno.JRubyMethod;
6253
import org.jruby.exceptions.RaiseException;
6354
import org.jruby.ext.openssl.x509store.X509Utils;
64-
import org.jruby.runtime.Arity;
65-
import org.jruby.runtime.Block;
66-
import org.jruby.runtime.ObjectAllocator;
67-
import org.jruby.runtime.ThreadContext;
55+
import org.jruby.runtime.*;
6856
import org.jruby.runtime.builtin.IRubyObject;
57+
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
58+
import org.jruby.runtime.callsite.RespondToCallSite;
6959
import org.jruby.util.ByteList;
70-
import org.jruby.runtime.Visibility;
7160

7261
import static org.jruby.ext.openssl.SSL.newSSLErrorWaitReadable;
7362
import static org.jruby.ext.openssl.SSL.newSSLErrorWaitWritable;
@@ -86,12 +75,45 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
8675
}
8776
};
8877

78+
private enum CallSiteIndex {
79+
80+
// self
81+
hostname("hostname"),
82+
//sync_close("sync_close"),
83+
//sync_close_w("sync_close="),
84+
// io
85+
_respond_to_nonblock_w("nonblock="),
86+
nonblock_w("nonblock="),
87+
sync("sync"),
88+
sync_w("sync="),
89+
flush("flush"),
90+
close("close"),
91+
closed_p("closed?"),
92+
// ssl_context
93+
verify_mode("verify_mode");
94+
95+
final String method;
96+
97+
CallSiteIndex(String method) { this.method = method; }
98+
99+
}
100+
89101
public static void createSSLSocket(final Ruby runtime, final RubyModule SSL) { // OpenSSL::SSL
102+
CallSiteIndex[] values = CallSiteIndex.values();
103+
CallSite[] extraCallSites = new CallSite[values.length];
104+
for (int i=0; i<values.length; i++) {
105+
if (values[i].name().startsWith("_respond_to")) {
106+
extraCallSites[i] = new RespondToCallSite();
107+
}
108+
else {
109+
extraCallSites[i] = new FunctionalCachingCallSite(values[i].method);
110+
}
111+
}
112+
113+
RubyClass SSLSocket = runtime.defineClassUnder("SSLSocket", runtime.getObject(), ALLOCATOR, SSL, extraCallSites);
114+
90115
final ThreadContext context = runtime.getCurrentContext();
91-
RubyClass SSLSocket = SSL.defineClassUnder("SSLSocket", runtime.getObject(), ALLOCATOR);
92-
// SSLSocket.addReadAttribute(context, "io");
93-
// SSLSocket.defineAlias("to_io", "io");
94-
// SSLSocket.addReadAttribute(context, "context");
116+
95117
SSLSocket.addReadWriteAttribute(context, "sync_close");
96118
SSLSocket.addReadWriteAttribute(context, "hostname");
97119
SSLSocket.defineAnnotatedMethods(SSLSocket.class);
@@ -123,6 +145,10 @@ private static RaiseException newSSLErrorFromHandshake(Ruby runtime, SSLHandshak
123145
return SSL.newSSLError(runtime, cause);
124146
}
125147

148+
private CallSite callSite(final CallSiteIndex index) {
149+
return getMetaClass().getExtraCallSites()[ index.ordinal() ];
150+
}
151+
126152
private SSLContext sslContext;
127153
private SSLEngine engine;
128154
private RubyIO io;
@@ -152,29 +178,36 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
152178
if ( ! ( args[0] instanceof RubyIO ) ) {
153179
throw runtime.newTypeError("IO expected but got " + args[0].getMetaClass().getName());
154180
}
155-
setInstanceVariable("@io", this.io = (RubyIO) args[0]); // compat (we do not read @io)
156-
// Ruby 2.3 : @io.nonblock = true if @io.respond_to?(:nonblock=)
157-
if (io.respondsTo("nonblock=")) {
158-
io.callMethod(context, "nonblock=", runtime.getTrue());
159-
}
160181
setInstanceVariable("@context", this.sslContext); // only compat (we do not use @context)
182+
setInstanceVariable("@io", this.io = (RubyIO) args[0]);
183+
set_io_nonblock_checked(context, runtime.getTrue());
161184
// This is a bit of a hack: SSLSocket should share code with
162185
// RubyBasicSocket, which always sets sync to true.
163186
// Instead we set it here for now.
164-
this.set_sync(context, runtime.getTrue()); // io.sync = true
165-
this.callMethod(context, "sync_close=", runtime.getFalse());
187+
set_sync(context, runtime.getTrue()); // io.sync = true
188+
setInstanceVariable("@sync_close", runtime.getFalse()); // self.sync_close = false
166189
sslContext.setup(context);
167190
return Utils.invokeSuper(context, this, args, Block.NULL_BLOCK); // super()
168191
}
169192

193+
private IRubyObject set_io_nonblock_checked(final ThreadContext context, RubyBoolean value) {
194+
// @io.nonblock = true if @io.respond_to?(:nonblock=)
195+
IRubyObject respond = callSite(CallSiteIndex._respond_to_nonblock_w).call(context, io, io, context.runtime.newSymbol("nonblock="));
196+
if (respond.isTrue()) {
197+
return callSite(CallSiteIndex.nonblock_w).call(context, io, io, value);
198+
}
199+
return context.nil;
200+
}
201+
170202
private SSLEngine ossl_ssl_setup(final ThreadContext context)
171203
throws NoSuchAlgorithmException, KeyManagementException {
172204
SSLEngine engine = this.engine;
173205
if ( engine != null ) return engine;
174206

175207
// Server Name Indication (SNI) RFC 3546
176208
// SNI support will not be attempted unless hostname is explicitly set by the caller
177-
String peerHost = this.callMethod(context, "hostname").toString();
209+
IRubyObject hostname = callSite(CallSiteIndex.hostname).call(context, this, this); // self.hostname
210+
String peerHost = hostname.toString();
178211
final int peerPort = socketChannelImpl().getRemotePort();
179212
engine = sslContext.createSSLEngine(peerHost, peerPort);
180213

@@ -199,12 +232,12 @@ private SSLEngine ossl_ssl_setup(final ThreadContext context)
199232

200233
@JRubyMethod(name = "sync")
201234
public IRubyObject sync(final ThreadContext context) {
202-
return this.io.callMethod(context, "sync");
235+
return callSite(CallSiteIndex.sync).call(context, io, io); // io.sync
203236
}
204237

205238
@JRubyMethod(name = "sync=")
206239
public IRubyObject set_sync(final ThreadContext context, final IRubyObject sync) {
207-
return this.io.callMethod(context, "sync=", sync);
240+
return callSite(CallSiteIndex.sync_w).call(context, io, io, sync); // io.sync = sync
208241
}
209242

210243
@JRubyMethod
@@ -302,8 +335,8 @@ private IRubyObject acceptImpl(final ThreadContext context, final boolean blocki
302335
if ( ! initialHandshake ) {
303336
final SSLEngine engine = ossl_ssl_setup(context);
304337
engine.setUseClientMode(false);
305-
final IRubyObject verify_mode = sslContext.callMethod(context, "verify_mode");
306-
if ( ! verify_mode.isNil() ) {
338+
final IRubyObject verify_mode = callSite(CallSiteIndex.verify_mode).call(context, sslContext, sslContext);
339+
if ( verify_mode != context.nil ) {
307340
final int verify = RubyNumeric.fix2int(verify_mode);
308341
if ( verify == 0 ) { // VERIFY_NONE
309342
engine.setNeedClientAuth(false);
@@ -863,7 +896,7 @@ private IRubyObject syswriteImpl(final ThreadContext context,
863896
written = write(buff, blocking);
864897
}
865898

866-
this.io.callMethod(context, "flush");
899+
callSite(CallSiteIndex.flush).call(context, io, io); // io.flush
867900

868901
return runtime.newFixnum(written);
869902
}
@@ -931,17 +964,22 @@ private void close(boolean force) {
931964

932965
@JRubyMethod
933966
public IRubyObject sysclose(final ThreadContext context) {
934-
//if ( isClosed() ) return context.runtime.getNil();
935-
if ( this.io.callMethod(context, "closed?").isTrue() ) {
936-
return context.runtime.getNil();
937-
} // Ruby 2.3
967+
if ( io_closed_p(context).isTrue() ) return context.nil;
968+
938969
// no need to try shutdown when it's a server
939970
close( sslContext.isProtocolForClient() );
940971

941-
if ( this.callMethod(context, "sync_close").isTrue() ) {
942-
return this.io.callMethod(context, "close");
943-
}
944-
return context.runtime.getNil();
972+
if ( getInstanceVariable("@sync_close").isTrue() ) return io_close(context);
973+
974+
return context.nil;
975+
}
976+
977+
private IRubyObject io_closed_p(final ThreadContext context) { // io.closed?
978+
return callSite(CallSiteIndex.closed_p).call(context, io, io);
979+
}
980+
981+
private IRubyObject io_close(final ThreadContext context) { // io.close
982+
return callSite(CallSiteIndex.close).call(context, io, io);
945983
}
946984

947985
@JRubyMethod

src/test/ruby/ssl/test_socket.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ def test_attr_methods
1818

1919
assert socket.io
2020
assert_equal socket.io, socket.to_io
21-
assert ! socket.respond_to?(:'io=')
21+
assert ! socket.respond_to?('io=')
2222
# due compatibility :
2323
assert_equal socket.io, socket.instance_variable_get(:@io)
2424

2525
assert socket.context
26-
assert ! socket.respond_to?(:'context=')
26+
assert ! socket.respond_to?('context=')
2727
# due compatibility :
2828
assert_equal socket.context, socket.instance_variable_get(:@context)
2929

0 commit comments

Comments
 (0)