Skip to content

Commit

Permalink
Showing 122 changed files with 1,909 additions and 1,214 deletions.
8 changes: 6 additions & 2 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -1609,8 +1609,10 @@ private void initExceptions() {
ioError = defineClassIfAllowed("IOError", standardError);
scriptError = defineClassIfAllowed("ScriptError", exceptionClass);
rangeError = defineClassIfAllowed("RangeError", standardError);
signalException = defineClassIfAllowed("SignalException", exceptionClass);

if (profile.allowClass("SignalException")) {
signalException = RubySignalException.createSignalExceptionClass(this, exceptionClass);
}
if (profile.allowClass("NameError")) {
nameError = RubyNameError.createNameErrorClass(this, standardError);
nameErrorMessage = RubyNameError.createNameErrorMessageClass(this, nameError);
@@ -1632,7 +1634,9 @@ private void initExceptions() {
}

fatal = defineClassIfAllowed("Fatal", exceptionClass);
interrupt = defineClassIfAllowed("Interrupt", signalException);
if (profile.allowClass("Interrupt")) {
interrupt = RubyInterrupt.createInterruptClass(this, signalException);
}
typeError = defineClassIfAllowed("TypeError", standardError);
argumentError = defineClassIfAllowed("ArgumentError", standardError);
if (profile.allowClass("UncaughtThrowError")) {
6 changes: 6 additions & 0 deletions core/src/main/java/org/jruby/RubyFile.java
Original file line number Diff line number Diff line change
@@ -298,6 +298,12 @@ protected IRubyObject rbIoClose(Ruby runtime) {

@JRubyMethod(required = 1)
public IRubyObject flock(ThreadContext context, IRubyObject operation) {

// Solaris uses a ruby-ffi version defined in jruby/kernel/file.rb, so re-dispatch
if (org.jruby.platform.Platform.IS_SOLARIS) {
return callMethod(context, "flock", operation);
}

Ruby runtime = context.runtime;
OpenFile fptr;
// int[] op = {0,0};
78 changes: 78 additions & 0 deletions core/src/main/java/org/jruby/RubyInterrupt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

package org.jruby;

import static jnr.constants.platform.Signal.SIGINT;

import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Arity;
import org.jruby.runtime.builtin.IRubyObject;
import static org.jruby.runtime.Visibility.PRIVATE;
import org.jruby.util.ArraySupport;

@JRubyClass(name="Interrupt", parent="SignalException")
public class RubyInterrupt extends RubySignalException {
private static final ObjectAllocator INTERRUPT_ALLOCATOR = new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyInterrupt(runtime, klass);
}
};

static RubyClass createInterruptClass(Ruby runtime, RubyClass signalExceptionClass) {
RubyClass interruptClass = runtime.defineClass("Interrupt", signalExceptionClass, INTERRUPT_ALLOCATOR);
interruptClass.defineAnnotatedMethods(RubyInterrupt.class);
return interruptClass;
}

protected RubyInterrupt(Ruby runtime, RubyClass exceptionClass) {
super(runtime, exceptionClass);
}

@JRubyMethod(optional = 1, visibility = PRIVATE)
@Override
public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block block) {
final Ruby runtime = context.runtime;

Arity.checkArgumentCount(runtime, args, 0, 1);

IRubyObject signo = runtime.newFixnum(SIGINT);

if (args.length > 0) {
args = ArraySupport.newCopy(signo, args);
} else {
args = new IRubyObject[]{signo, runtime.newString("Interrupt")};
}

super.initialize(args, block);
return this;
}
}
28 changes: 23 additions & 5 deletions core/src/main/java/org/jruby/RubySignal.java
Original file line number Diff line number Diff line change
@@ -74,9 +74,9 @@ public static Map<String, Integer> list() {
Map<String, Integer> signals = new HashMap<String, Integer>();

for (Signal s : Signal.values()) {
if (!s.description().startsWith("SIG"))
if (!s.description().startsWith(SIGNAME_PREFIX))
continue;
if (!RUBY_18_SIGNALS.contains(s.description().substring(3)))
if (!RUBY_18_SIGNALS.contains(signmWithoutPrefix(s.description())))
continue;

// replace CLD with CHLD value
@@ -88,7 +88,7 @@ public static Map<String, Integer> list() {
if (signo >= 20000)
continue;

signals.put(s.description().substring("SIG".length()), signo);
signals.put(signmWithoutPrefix(s.description()), signo);
}

return signals;
@@ -143,12 +143,28 @@ public static IRubyObject signame(ThreadContext context, final IRubyObject recv,
public static String signo2signm(long no) {
for (Signal s : Signal.values()) {
if (s.intValue() == no) {
return s.name().substring(3);
return signmWithoutPrefix(s.name());
}
}
return null;
}


// MRI: signm2signo
public static long signm2signo(String nm) {
for (Signal s : Signal.values()) {
if (signmWithoutPrefix(s.name()).equals(nm)) return s.longValue();
}
return 0;
}

public static String signmWithPrefix(String nm) {
return (nm.startsWith(SIGNAME_PREFIX)) ? nm : SIGNAME_PREFIX + nm;
}

public static String signmWithoutPrefix(String nm) {
return (nm.startsWith(SIGNAME_PREFIX)) ? nm.substring(SIGNAME_PREFIX.length()) : nm;
}

private static final Set<String> RUBY_18_SIGNALS;
static {
RUBY_18_SIGNALS = new HashSet<String>();
@@ -201,4 +217,6 @@ public static String signo2signm(long no) {
RUBY_18_SIGNALS.add(name);
}
}

private static final String SIGNAME_PREFIX = "SIG";
}// RubySignal
127 changes: 127 additions & 0 deletions core/src/main/java/org/jruby/RubySignalException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

package org.jruby;

import static jnr.constants.platform.Signal.NSIG;

import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import static org.jruby.runtime.Visibility.PRIVATE;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.RubyBasicObject;
import org.jruby.RubySignal;
import org.jruby.util.TypeConverter;

@JRubyClass(name="SignalException", parent="Exception")
public class RubySignalException extends RubyException {
private static final ObjectAllocator SIGNAL_EXCEPTION_ALLOCATOR = new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubySignalException(runtime, klass);
}
};

protected RubySignalException(Ruby runtime, RubyClass exceptionClass) {
super(runtime, exceptionClass);
}

static RubyClass createSignalExceptionClass(Ruby runtime, RubyClass exceptionClass) {
RubyClass signalExceptionClass = runtime.defineClass("SignalException", exceptionClass, SIGNAL_EXCEPTION_ALLOCATOR);
signalExceptionClass.defineAnnotatedMethods(RubySignalException.class);

return signalExceptionClass;
}

@JRubyMethod(optional = 2, visibility = PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block block) {
final Ruby runtime = context.runtime;
int argnum = 1;
IRubyObject sig = context.nil;
long _signo;
int argc = args.length;

if (argc > 0) {
sig = TypeConverter.checkIntegerType(runtime, args[0], "to_int");

if (sig.isNil()) {
sig = args[0];
} else {
argnum = 2;
}
}

Arity.checkArgumentCount(runtime, args, 1, argnum);

if (argnum == 2) {
_signo = sig.convertToInteger().getLongValue();
if (_signo < 0 || _signo > NSIG.longValue()) {
throw runtime.newArgumentError("invalid signal number (" + _signo + ")");
}

if (argc > 1) {
sig = args[1];
} else {
sig = runtime.newString(RubySignal.signmWithPrefix(RubySignal.signo2signm(_signo)));
}
} else {
String signm = sig.toString();
_signo = RubySignal.signm2signo(RubySignal.signmWithoutPrefix(signm));

if (_signo == 0) {
throw runtime.newArgumentError("unsupported name " + sig);
}

sig = runtime.newString(RubySignal.signmWithPrefix(signm));
}

super.initialize(new IRubyObject[]{sig}, block);
this.signo = runtime.newFixnum(_signo);

return this;
}

@JRubyMethod
public IRubyObject signo(ThreadContext context) {
assert signo != null;

if (signo == RubyBasicObject.UNDEF) return context.nil;

return signo;
}

@JRubyMethod(name = {"message","signm"})
@Override
public IRubyObject message(ThreadContext context) {
return super.message(context);
}

private IRubyObject signo;
}
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
@@ -213,6 +213,7 @@ protected RubyThread(Ruby runtime, RubyClass type) {
super(runtime, type);

finalResult = errorInfo = runtime.getNil();
threadName = runtime.getNil();
}

public RubyThread(Ruby runtime, RubyClass klass, Runnable runnable) {
34 changes: 17 additions & 17 deletions core/src/main/java/org/jruby/ext/etc/RubyEtc.java
Original file line number Diff line number Diff line change
@@ -114,7 +114,7 @@ private static IRubyObject intoStringArray(Ruby runtime, String[] members) {


@JRubyMethod(optional=1, module = true)
public static IRubyObject getpwuid(IRubyObject recv, IRubyObject[] args) {
public static synchronized IRubyObject getpwuid(IRubyObject recv, IRubyObject[] args) {
Ruby runtime = recv.getRuntime();
POSIX posix = runtime.getPosix();
IRubyObject oldExc = runtime.getGlobalVariables().get("$!"); // Save $!
@@ -143,7 +143,7 @@ public static IRubyObject getpwuid(IRubyObject recv, IRubyObject[] args) {
}

@JRubyMethod(required=1, module = true)
public static IRubyObject getpwnam(IRubyObject recv, IRubyObject name) {
public static synchronized IRubyObject getpwnam(IRubyObject recv, IRubyObject name) {
Ruby runtime = recv.getRuntime();
String nam = name.convertToString().toString();
try {
@@ -166,7 +166,7 @@ public static IRubyObject getpwnam(IRubyObject recv, IRubyObject name) {
}

@JRubyMethod(module = true)
public static IRubyObject passwd(IRubyObject recv, Block block) {
public static synchronized IRubyObject passwd(IRubyObject recv, Block block) {
Ruby runtime = recv.getRuntime();
POSIX posix = runtime.getPosix();
try {
@@ -206,7 +206,7 @@ public static IRubyObject passwd(IRubyObject recv, Block block) {
}

@JRubyMethod(module = true)
public static IRubyObject getlogin(IRubyObject recv) {
public static synchronized IRubyObject getlogin(IRubyObject recv) {
Ruby runtime = recv.getRuntime();

try {
@@ -228,7 +228,7 @@ public static IRubyObject getlogin(IRubyObject recv) {
}

@JRubyMethod(module = true)
public static IRubyObject endpwent(IRubyObject recv) {
public static synchronized IRubyObject endpwent(IRubyObject recv) {
Ruby runtime = recv.getRuntime();
try {
runtime.getPosix().endpwent();
@@ -241,7 +241,7 @@ public static IRubyObject endpwent(IRubyObject recv) {
}

@JRubyMethod(module = true)
public static IRubyObject setpwent(IRubyObject recv) {
public static synchronized IRubyObject setpwent(IRubyObject recv) {
Ruby runtime = recv.getRuntime();
try {
runtime.getPosix().setpwent();
@@ -254,7 +254,7 @@ public static IRubyObject setpwent(IRubyObject recv) {
}

@JRubyMethod(module = true)
public static IRubyObject getpwent(IRubyObject recv) {
public static synchronized IRubyObject getpwent(IRubyObject recv) {
Ruby runtime = recv.getRuntime();
try {
Passwd passwd = runtime.getPosix().getpwent();
@@ -272,7 +272,7 @@ public static IRubyObject getpwent(IRubyObject recv) {
}

@JRubyMethod(required=1, module = true)
public static IRubyObject getgrnam(IRubyObject recv, IRubyObject name) {
public static synchronized IRubyObject getgrnam(IRubyObject recv, IRubyObject name) {
Ruby runtime = recv.getRuntime();
String nam = name.convertToString().toString();
try {
@@ -295,7 +295,7 @@ public static IRubyObject getgrnam(IRubyObject recv, IRubyObject name) {
}

@JRubyMethod(optional=1, module = true)
public static IRubyObject getgrgid(IRubyObject recv, IRubyObject[] args) {
public static synchronized IRubyObject getgrgid(IRubyObject recv, IRubyObject[] args) {
Ruby runtime = recv.getRuntime();
POSIX posix = runtime.getPosix();

@@ -320,7 +320,7 @@ public static IRubyObject getgrgid(IRubyObject recv, IRubyObject[] args) {
}

@JRubyMethod(module = true)
public static IRubyObject endgrent(IRubyObject recv) {
public static synchronized IRubyObject endgrent(IRubyObject recv) {
Ruby runtime = recv.getRuntime();
try {
runtime.getPosix().endgrent();
@@ -333,7 +333,7 @@ public static IRubyObject endgrent(IRubyObject recv) {
}

@JRubyMethod(module = true)
public static IRubyObject setgrent(IRubyObject recv) {
public static synchronized IRubyObject setgrent(IRubyObject recv) {
Ruby runtime = recv.getRuntime();
try {
runtime.getPosix().setgrent();
@@ -346,7 +346,7 @@ public static IRubyObject setgrent(IRubyObject recv) {
}

@JRubyMethod(module = true)
public static IRubyObject group(IRubyObject recv, Block block) {
public static synchronized IRubyObject group(IRubyObject recv, Block block) {
Ruby runtime = recv.getRuntime();
POSIX posix = runtime.getPosix();

@@ -391,7 +391,7 @@ public static IRubyObject group(IRubyObject recv, Block block) {
}

@JRubyMethod(module = true)
public static IRubyObject getgrent(IRubyObject recv) {
public static synchronized IRubyObject getgrent(IRubyObject recv) {
Ruby runtime = recv.getRuntime();
try {
Group gr;
@@ -416,7 +416,7 @@ public static IRubyObject getgrent(IRubyObject recv) {
}

@JRubyMethod(module = true)
public static IRubyObject systmpdir(ThreadContext context, IRubyObject recv) {
public static synchronized IRubyObject systmpdir(ThreadContext context, IRubyObject recv) {
Ruby runtime = context.getRuntime();
ByteList tmp = ByteList.create(System.getProperty("java.io.tmpdir")); // default for all platforms except Windows
if (Platform.IS_WINDOWS) {
@@ -430,7 +430,7 @@ public static IRubyObject systmpdir(ThreadContext context, IRubyObject recv) {
}

@JRubyMethod(module = true)
public static IRubyObject sysconfdir(ThreadContext context, IRubyObject recv) {
public static synchronized IRubyObject sysconfdir(ThreadContext context, IRubyObject recv) {
Ruby runtime = context.getRuntime();
ByteList tmp = ByteList.create(RbConfigLibrary.getSysConfDir(runtime)); // default for all platforms except Windows

@@ -446,13 +446,13 @@ public static IRubyObject sysconfdir(ThreadContext context, IRubyObject recv) {
}

@JRubyMethod(module = true)
public static IRubyObject nprocessors(ThreadContext context, IRubyObject recv) {
public static synchronized IRubyObject nprocessors(ThreadContext context, IRubyObject recv) {
int nprocs = Runtime.getRuntime().availableProcessors();
return RubyFixnum.newFixnum(context.getRuntime(), nprocs);
}

@JRubyMethod(module = true)
public static IRubyObject uname(ThreadContext context, IRubyObject self) {
public static synchronized IRubyObject uname(ThreadContext context, IRubyObject self) {
Ruby runtime = context.runtime;
RubyHash uname = RubyHash.newHash(runtime);

2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ext/ripper/HeredocTerm.java
Original file line number Diff line number Diff line change
@@ -155,7 +155,7 @@ public int parseString(RipperLexer lexer, LexerSource src) throws java.io.IOExce
lexer.lex_goto_eol();

if (lexer.getHeredocIndent() > 0) {
lexer.setValue(str);
lexer.setValue(lexer.createStr(str, 0));
return Tokens.tSTRING_CONTENT;
}
// MRI null checks str in this case but it is unconditionally non-null?
560 changes: 401 additions & 159 deletions core/src/main/java/org/jruby/ext/socket/Addrinfo.java

Large diffs are not rendered by default.

108 changes: 83 additions & 25 deletions core/src/main/java/org/jruby/ext/socket/Option.java
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
import org.jruby.util.Sprintf;
import org.jruby.util.TypeConverter;

import java.nio.ByteBuffer;
import java.text.NumberFormat;
@@ -42,19 +43,17 @@ public Option(Ruby runtime, RubyClass klass) {
super(runtime, klass);
}

public Option(Ruby runtime, ProtocolFamily family, SocketLevel level, SocketOption option, int data) {
public Option(Ruby runtime, ProtocolFamily family, SocketLevel level, SocketOption option, ByteList data) {
this(runtime, (RubyClass)runtime.getClassFromPath("Socket::Option"), family, level, option, data);
}

public Option(Ruby runtime, RubyClass klass, ProtocolFamily family, SocketLevel level, SocketOption option, int data) {
public Option(Ruby runtime, RubyClass klass, ProtocolFamily family, SocketLevel level, SocketOption option, ByteList data) {
super(runtime, klass);

this.family = family;
this.level = level;
this.option = option;
this.intData = data;
ByteList result = new ByteList(4);
this.data = Pack.packInt_i(result, data);
this.data = data;
}

@JRubyMethod(required = 4, visibility = Visibility.PRIVATE)
@@ -63,7 +62,7 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
level = SocketUtils.levelFromArg(args[1]);
option = SocketUtils.optionFromArg(args[2]);
data = args[3].convertToString().getByteList();
intData = Pack.unpackInt_i(ByteBuffer.wrap(data.bytes()));

return this;
}

@@ -95,7 +94,7 @@ public IRubyObject inspect(ThreadContext context) {

buf
.append(metaClass.getRealClass().getName())
.append(' ')
.append(": ")
.append(noPrefix(family));

if (level == SocketLevel.SOL_SOCKET) {
@@ -146,65 +145,117 @@ private String optionValue() {
case SO_DONTROUTE:
case SO_RCVLOWAT:
case SO_SNDLOWAT:
return String.valueOf(intData);
return String.valueOf(unpackInt(data));

case SO_LINGER:
return intData == -1 ? "off" :
intData == 0 ? "on" :
"on(" + intData + ")";
int[] linger = Option.unpackLinger(data);

return ((linger[0] == 0) ? "off " : "on ") + linger[1] + "sec";

case SO_RCVTIMEO:
case SO_SNDTIMEO:
return Sprintf.getNumberFormat(Locale.getDefault()).format(intData / 1000.0);
return Sprintf.getNumberFormat(Locale.getDefault()).format(unpackInt(data) / 1000.0);

case SO_ERROR:
return Errno.valueOf(intData).description();
return Errno.valueOf(unpackInt(data)).description();

case SO_TYPE:
return Sock.valueOf(intData).description();
return Sock.valueOf(unpackInt(data)).description();
}

return "";
}

public static ByteList packInt(int i) {
ByteList result = new ByteList(4);
Pack.packInt_i(result, i);
return result;
}

public static ByteList packLinger(int vonoff, int vsecs) {
ByteList result = new ByteList(8);
Pack.packInt_i(result, vonoff);
Pack.packInt_i(result, vsecs);
return result;
}

public static int unpackInt(ByteList data) {
return Pack.unpackInt_i(ByteBuffer.wrap(data.unsafeBytes(), data.begin(), data.realSize()));
}

public static int[] unpackLinger(ByteList data) {
ByteList result = new ByteList(8);
ByteBuffer buf = ByteBuffer.wrap(data.unsafeBytes(), data.begin(), data.realSize());
int vonoff = Pack.unpackInt_i(buf);
int vsecs = Pack.unpackInt_i(buf);
return new int[] {vonoff, vsecs};
}

@JRubyMethod(name = "int", required = 4, meta = true)
public static IRubyObject rb_int(ThreadContext context, IRubyObject self, IRubyObject[] args) {
ProtocolFamily family = SocketUtils.protocolFamilyFromArg(args[0]);
SocketLevel level = SocketUtils.levelFromArg(args[1]);
SocketOption option = SocketUtils.optionFromArg(args[2]);
int intData = RubyNumeric.fix2int(args[3]);
ByteList data = packInt(RubyNumeric.fix2int(args[3]));

return new Option(context.getRuntime(), family, level, option, intData);
return new Option(context.getRuntime(), family, level, option, data);
}

@JRubyMethod(name = "int")
public IRubyObject asInt(ThreadContext context) {
return context.getRuntime().newFixnum((int) intData);
final Ruby runtime = context.getRuntime();

validateDataSize(runtime, data, 4);

return runtime.newFixnum(unpackInt(data));
}

@JRubyMethod(required = 4, meta = true)
public static IRubyObject bool(ThreadContext context, IRubyObject self, IRubyObject[] args) {
ProtocolFamily family = SocketUtils.protocolFamilyFromArg(args[0]);
SocketLevel level = SocketUtils.levelFromArg(args[1]);
SocketOption option = SocketUtils.optionFromArg(args[2]);
int intData = args[3].isTrue() ? 1 : 0;
ByteList data = packInt(args[3].isTrue() ? 1 : 0);

return new Option(context.getRuntime(), family, level, option, intData);
return new Option(context.getRuntime(), family, level, option, data);
}

@JRubyMethod
public IRubyObject bool(ThreadContext context) {
return context.getRuntime().newBoolean(intData != 0);
final Ruby runtime = context.getRuntime();

validateDataSize(runtime, data, 4);

return runtime.newBoolean(unpackInt(data) != 0);
}

@JRubyMethod(meta = true)
public IRubyObject linger(ThreadContext context, IRubyObject self) {
return context.nil;
}
public static IRubyObject linger(ThreadContext context, IRubyObject self, IRubyObject vonoff, IRubyObject vsecs) {
ProtocolFamily family = ProtocolFamily.PF_UNSPEC;
SocketLevel level = SocketLevel.SOL_SOCKET;
SocketOption option = SocketOption.SO_LINGER;
int coercedVonoff;

if (!TypeConverter.checkIntegerType(context, vonoff).isNil()) {
coercedVonoff = vonoff.convertToInteger().getIntValue();
} else {
coercedVonoff = vonoff.isTrue() ? 1 : 0;
}

ByteList data = packLinger(coercedVonoff, vsecs.convertToInteger().getIntValue());

return new Option(context.getRuntime(), family, level, option, data);
}

@JRubyMethod
public IRubyObject linger(ThreadContext context) {
return context.nil;
final Ruby runtime = context.runtime;

validateDataSize(runtime, data, 8);

int[] linger = Option.unpackLinger(data);

return runtime.newArray(runtime.newBoolean(linger[0] != 0), runtime.newFixnum(linger[1]));
}

@JRubyMethod
@@ -217,9 +268,16 @@ public IRubyObject to_s(ThreadContext context) {
return RubyString.newString(context.runtime, data);
}

private static void validateDataSize(Ruby runtime, ByteList data, int size) {
int realSize = data.realSize();

if (realSize != size) {
throw runtime.newTypeError("size differ. expected as sizeof(int)=" + size + " but " + realSize);
}
}

private ProtocolFamily family;
private SocketLevel level;
private SocketOption option;
private ByteList data;
private long intData;
}
188 changes: 110 additions & 78 deletions core/src/main/java/org/jruby/ext/socket/RubyBasicSocket.java
Original file line number Diff line number Diff line change
@@ -35,7 +35,6 @@
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.DatagramChannel;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectableChannel;

import jnr.constants.platform.Fcntl;
@@ -44,6 +43,7 @@
import jnr.constants.platform.SocketLevel;
import jnr.constants.platform.SocketOption;

import jnr.unixsocket.UnixSocketAddress;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
@@ -83,6 +83,7 @@ static void createBasicSocket(Ruby runtime) {
RubyClass rb_cBasicSocket = runtime.defineClass("BasicSocket", runtime.getIO(), BASICSOCKET_ALLOCATOR);

rb_cBasicSocket.defineAnnotatedMethods(RubyBasicSocket.class);
rb_cBasicSocket.undefineMethod("initialize");
}

private static ObjectAllocator BASICSOCKET_ALLOCATOR = new ObjectAllocator() {
@@ -179,15 +180,15 @@ public IRubyObject recv(ThreadContext context, IRubyObject[] args) {

@Deprecated
public IRubyObject recv(ThreadContext context, IRubyObject length, IRubyObject flags) {
return recv(context, new IRubyObject[] { length, flags });
return recv(context, new IRubyObject[]{length, flags});
}

private IRubyObject recv(ThreadContext context, IRubyObject length,
RubyString str, IRubyObject flags) {
// TODO: implement flags
final ByteBuffer buffer = ByteBuffer.allocate(RubyNumeric.fix2int(length));

ByteList bytes = doReceive(context, buffer);
ByteList bytes = doRead(context, buffer);

if (bytes == null) return context.nil;

@@ -198,11 +199,6 @@ private IRubyObject recv(ThreadContext context, IRubyObject length,
return RubyString.newString(context.runtime, bytes);
}

@JRubyMethod
public IRubyObject recv_nonblock(ThreadContext context, IRubyObject length) {
return recv_nonblock(context, length, context.nil, /* str */ null, true);
}

@JRubyMethod(required = 1, optional = 3) // (length) required = 1 handled above
public IRubyObject recv_nonblock(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
@@ -219,20 +215,14 @@ public IRubyObject recv_nonblock(ThreadContext context, IRubyObject[] args) {
case 1: length = args[0];
}

boolean exception = ArgsUtil.extractKeywordArg(context, "exception", opts) != runtime.getFalse();

return recv_nonblock(context, length, flags, str, exception);
}

protected final IRubyObject recv_nonblock(ThreadContext context,
IRubyObject length, IRubyObject flags, IRubyObject str, final boolean exception) {
boolean ex = ArgsUtil.extractKeywordArg(context, "exception", opts) != runtime.getFalse();
// TODO: implement flags
final ByteBuffer buffer = ByteBuffer.allocate(RubyNumeric.fix2int(length));

ByteList bytes = doReceiveNonblock(context, buffer);
ByteList bytes = doReadNonblock(context, buffer);

if (bytes == null) {
if (!exception) return context.runtime.newSymbol("wait_readable");
if (!ex) return context.runtime.newSymbol("wait_readable");
throw context.runtime.newErrnoEAGAINReadableError("recvfrom(2)");
}

@@ -266,8 +256,21 @@ public IRubyObject getsockopt(ThreadContext context, IRubyObject _level, IRubyOb
}

int value = SocketType.forChannel(channel).getSocketOption(channel, opt);
ByteList packedValue;

return new Option(runtime, ProtocolFamily.PF_INET, level, opt, value);
if (opt == SocketOption.SO_LINGER) {
if (value == -1) {
// Hardcode to 0, because Java Socket API drops actual value
// if lingering is disabled
packedValue = Option.packLinger(0, 0);
} else {
packedValue = Option.packLinger(1, value);
}
} else {
packedValue = Option.packInt(value);
}

return new Option(runtime, ProtocolFamily.PF_INET, level, opt, packedValue);

default:
throw runtime.newErrnoENOPROTOOPTError();
@@ -278,6 +281,16 @@ public IRubyObject getsockopt(ThreadContext context, IRubyObject _level, IRubyOb
}
}

@JRubyMethod
public IRubyObject setsockopt(ThreadContext context, IRubyObject option) {
if (option instanceof Option) {
Option rsockopt = (Option) option;
return setsockopt(context, rsockopt.level(context), rsockopt.optname(context), rsockopt.data(context));
} else {
throw context.runtime.newArgumentError(option.toString() + " is not a Socket::Option");
}
}

@JRubyMethod
public IRubyObject setsockopt(ThreadContext context, IRubyObject _level, IRubyObject _opt, IRubyObject val) {
Ruby runtime = context.runtime;
@@ -297,17 +310,12 @@ public IRubyObject setsockopt(ThreadContext context, IRubyObject _level, IRubyOb
case SOL_UDP:

if (opt == SocketOption.SO_LINGER) {
if(val instanceof RubyBoolean && !val.isTrue()) {
socketType.setSoLinger(channel, false, 0);
if (val instanceof RubyString) {
int[] linger = Option.unpackLinger(val.convertToString().getByteList());
socketType.setSoLinger(channel, linger[0] != 0, linger[1]);
} else {
int num = asNumber(val);
if(num == -1) {
socketType.setSoLinger(channel, false, 0);
} else {
socketType.setSoLinger(channel, true, num);
}
throw runtime.newErrnoEINVALError("setsockopt(2)");
}

} else {
socketType.setSocketOption(channel, opt, asNumber(val));
}
@@ -348,18 +356,10 @@ public IRubyObject getsockname(ThreadContext context) {
public IRubyObject getpeername(ThreadContext context) {
Ruby runtime = context.runtime;

try {
SocketAddress sock = getRemoteSocket();

if (sock == null) {
throw runtime.newIOError("Not Supported");
}

return runtime.newString(sock.toString());
}
catch (BadDescriptorException e) {
throw runtime.newErrnoEBADFError();
}
InetSocketAddress sock = getInetRemoteSocket();
if (sock != null) return runtime.newString(sock.getHostName());
UnixSocketAddress unix = getUnixRemoteSocket();
return runtime.newString(unix.path());
}

@JRubyMethod(name = "getpeereid", notImplemented = true)
@@ -369,30 +369,37 @@ public IRubyObject getpeereid(ThreadContext context) {

@JRubyMethod
public IRubyObject local_address(ThreadContext context) {
try {
InetSocketAddress address = getSocketAddress();
Ruby runtime = context.runtime;

if (address == null) return context.nil;
InetSocketAddress address = getInetSocketAddress();

return new Addrinfo(context.runtime, context.runtime.getClass("Addrinfo"), address.getAddress(), address.getPort(), SocketType.forChannel(getChannel()));
}
catch (BadDescriptorException e) {
throw context.runtime.newErrnoEBADFError("address unavailable");
if (address != null) {
SocketType socketType = SocketType.forChannel(getChannel());
return new Addrinfo(runtime, runtime.getClass("Addrinfo"), address, socketType.getSocketType(), socketType);
}

UnixSocketAddress unix = getUnixSocketAddress();
return Addrinfo.unix(context, runtime.getClass("Addrinfo"), runtime.newString(unix.path()));
}

@JRubyMethod
public IRubyObject remote_address(ThreadContext context) {
try {
InetSocketAddress address = getRemoteSocket();
Ruby runtime = context.runtime;

if (address == null) return context.nil;
InetSocketAddress address = getInetRemoteSocket();

return new Addrinfo(context.runtime, context.runtime.getClass("Addrinfo"), address.getAddress(), address.getPort(), SocketType.forChannel(getChannel()));
}
catch (BadDescriptorException e) {
throw context.runtime.newErrnoEBADFError("address unavailable");
if (address != null) {
SocketType socketType = SocketType.forChannel(getChannel());
return new Addrinfo(runtime, runtime.getClass("Addrinfo"), address, socketType.getSocketType(), socketType);
}

UnixSocketAddress unix = getUnixRemoteSocket();

if (unix != null) {
return Addrinfo.unix(context, runtime.getClass("Addrinfo"), runtime.newString(unix.path()));
}

throw runtime.newErrnoENOTCONNError();
}

@JRubyMethod(optional = 1)
@@ -401,12 +408,9 @@ public IRubyObject shutdown(ThreadContext context, IRubyObject[] args) {

if (args.length > 0) {
String howString = null;
if (args[0] instanceof RubyString) {
howString = ((RubyString) args[0]).asJavaString();
} else if (args[0] instanceof RubySymbol) {
howString = ((RubySymbol) args[0]).asJavaString();
if (args[0] instanceof RubyString || args[0] instanceof RubySymbol) {
howString = args[0].asJavaString();
}

if (howString != null) {
if (howString.equals("RD") || howString.equals("SHUT_RD")) {
how = 0;
@@ -497,7 +501,7 @@ public IRubyObject readmsg_nonblock(ThreadContext context, IRubyObject[] args) {
throw context.runtime.newNotImplementedError("readmsg_nonblock is not implemented");
}

private ByteList doReceive(ThreadContext context, final ByteBuffer buffer) {
protected ByteList doRead(ThreadContext context, final ByteBuffer buffer) {
OpenFile fptr;

fptr = getOpenFile();
@@ -526,7 +530,7 @@ private ByteList doReceive(ThreadContext context, final ByteBuffer buffer) {
}
}

protected final ByteList doReceiveNonblock(ThreadContext context, final ByteBuffer buffer) {
protected final ByteList doReadNonblock(ThreadContext context, final ByteBuffer buffer) {
Channel channel = getChannel();

if ( ! (channel instanceof SelectableChannel) ) {
@@ -542,7 +546,7 @@ protected final ByteList doReceiveNonblock(ThreadContext context, final ByteBuff
selectable.configureBlocking(false);

try {
return doReceive(context, buffer);
return doRead(context, buffer);
}
finally {
selectable.configureBlocking(oldBlocking);
@@ -570,35 +574,52 @@ private void joinMulticastGroup(IRubyObject val) throws IOException, BadDescript
}
}

protected InetSocketAddress getSocketAddress() throws BadDescriptorException {
Channel channel = getOpenChannel();
protected InetSocketAddress getInetSocketAddress() {
SocketAddress socketAddress = getSocketAddress();
if (socketAddress instanceof InetSocketAddress) return (InetSocketAddress) socketAddress;
return null;
}

return (InetSocketAddress)SocketType.forChannel(channel).getLocalSocketAddress(channel);
protected InetSocketAddress getInetRemoteSocket() {
SocketAddress socketAddress = getRemoteSocket();
if (socketAddress instanceof InetSocketAddress) return (InetSocketAddress) socketAddress;
return null;
}

protected InetSocketAddress getRemoteSocket() throws BadDescriptorException {
Channel channel = getOpenChannel();
protected UnixSocketAddress getUnixSocketAddress() {
SocketAddress socketAddress = getSocketAddress();
if (socketAddress instanceof UnixSocketAddress) return (UnixSocketAddress) socketAddress;
return null;
}

return (InetSocketAddress)SocketType.forChannel(channel).getRemoteSocketAddress(channel);
protected UnixSocketAddress getUnixRemoteSocket() {
SocketAddress socketAddress = getRemoteSocket();
if (socketAddress instanceof UnixSocketAddress) return (UnixSocketAddress) socketAddress;
return null;
}

protected Sock getDefaultSocketType() {
return Sock.SOCK_STREAM;
protected SocketAddress getSocketAddress() {
Channel channel = getOpenChannel();

return SocketType.forChannel(channel).getLocalSocketAddress(channel);
}

protected IRubyObject getSocknameCommon(ThreadContext context, String caller) {
try {
InetSocketAddress sock = getSocketAddress();
protected SocketAddress getRemoteSocket() {
Channel channel = getOpenChannel();

if (sock == null) {
return Sockaddr.pack_sockaddr_in(context, 0, "0.0.0.0");
}
return SocketType.forChannel(channel).getRemoteSocketAddress(channel);
}

return Sockaddr.pack_sockaddr_in(context, sock);
protected IRubyObject getSocknameCommon(ThreadContext context, String caller) {
if (getInetSocketAddress() != null) {
return Sockaddr.pack_sockaddr_in(context, getInetSocketAddress());
}
catch (BadDescriptorException e) {
throw context.runtime.newErrnoEBADFError();

if (getUnixSocketAddress() != null) {
return Sockaddr.pack_sockaddr_un(context, getUnixSocketAddress().path());
}

return Sockaddr.pack_sockaddr_in(context, 0, "0.0.0.0");
}

private IRubyObject shutdownInternal(ThreadContext context, int how) throws BadDescriptorException {
@@ -767,4 +788,15 @@ public static IRubyObject set_do_not_reverse_lookup(IRubyObject recv, IRubyObjec

// By default we always reverse lookup unless do_not_reverse_lookup set.
private boolean doNotReverseLookup = false;

protected static class ReceiveTuple {
ReceiveTuple() {}
ReceiveTuple(RubyString result, InetSocketAddress sender) {
this.result = result;
this.sender = sender;
}

RubyString result;
InetSocketAddress sender;
}
}// RubyBasicSocket
158 changes: 82 additions & 76 deletions core/src/main/java/org/jruby/ext/socket/RubyIPSocket.java
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
*
* Copyright (C) 2007-2010 JRuby Team <team@jruby.org>
* Copyright (C) 2007 Ola Bini <ola@ologix.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -31,12 +31,14 @@
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
import org.jruby.RubySymbol;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Arity;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.TypeConverter;
import org.jruby.util.io.BadDescriptorException;
import org.jruby.util.io.Sockaddr;

@@ -49,12 +51,13 @@
public class RubyIPSocket extends RubyBasicSocket {
static void createIPSocket(Ruby runtime) {
RubyClass rb_cIPSocket = runtime.defineClass("IPSocket", runtime.getClass("BasicSocket"), IPSOCKET_ALLOCATOR);

rb_cIPSocket.defineAnnotatedMethods(RubyIPSocket.class);
rb_cIPSocket.undefineMethod("initialize");

runtime.getObject().setConstant("IPsocket",rb_cIPSocket);
}

private static ObjectAllocator IPSOCKET_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyIPSocket(runtime, klass);
@@ -65,32 +68,24 @@ public RubyIPSocket(Ruby runtime, RubyClass type) {
super(runtime, type);
}

public IRubyObject addr(ThreadContext context) {
return addrCommon(context, true);
}

@JRubyMethod(name = "addr")
public IRubyObject addr19(ThreadContext context) {
return addrCommon(context, true);
public IRubyObject addr(ThreadContext context) {
return addrCommon(context, !context.getRuntime().isDoNotReverseLookupEnabled());
}

@JRubyMethod(name = "addr")
public IRubyObject addr19(ThreadContext context, IRubyObject reverse) {
return addrCommon(context, reverse.isTrue());
public IRubyObject addr(ThreadContext context, IRubyObject reverse) {
return addrCommon(context, reverse);
}

public IRubyObject peeraddr(ThreadContext context) {
return peeraddrCommon(context, true);
}

@JRubyMethod(name = "peeraddr")
public IRubyObject peeraddr19(ThreadContext context) {
return peeraddrCommon(context, true);
public IRubyObject peeraddr(ThreadContext context) {
return peeraddrCommon(context, !context.getRuntime().isDoNotReverseLookupEnabled());
}

@JRubyMethod(name = "peeraddr")
public IRubyObject peeraddr19(ThreadContext context, IRubyObject reverse) {
return peeraddrCommon(context, reverse.isTrue());
public IRubyObject peeraddr(ThreadContext context, IRubyObject reverse) {
return peeraddrCommon(context, reverse);
}

@JRubyMethod(meta = true)
@@ -102,35 +97,31 @@ public static IRubyObject getaddress(ThreadContext context, IRubyObject self, IR
public IRubyObject recvfrom(ThreadContext context, IRubyObject _length) {
Ruby runtime = context.runtime;

try {
IRubyObject result = recv(context, _length);
InetSocketAddress sender = getRemoteSocket();

int port;
String hostName;
String hostAddress;

if (sender == null) {
port = 0;
hostName = hostAddress = "0.0.0.0";
} else {
port = sender.getPort();
hostName = sender.getHostName();
hostAddress = sender.getAddress().getHostAddress();
}
IRubyObject result = recv(context, _length);
InetSocketAddress sender = getInetRemoteSocket();

IRubyObject addressArray = RubyArray.newArray(
runtime,
runtime.newString("AF_INET"),
runtime.newFixnum(port),
runtime.newString(hostName),
runtime.newString(hostAddress));
int port;
String hostName;
String hostAddress;

return RubyArray.newArray(runtime, result, addressArray);

} catch (BadDescriptorException e) {
throw runtime.newErrnoEBADFError();
if (sender == null) {
port = 0;
hostName = hostAddress = "0.0.0.0";
} else {
port = sender.getPort();
hostName = sender.getHostName();
hostAddress = sender.getAddress().getHostAddress();
}

IRubyObject addressArray = context.runtime.newArray(
new IRubyObject[]{
runtime.newString("AF_INET"),
runtime.newFixnum(port),
runtime.newString(hostName),
runtime.newString(hostAddress)
});

return runtime.newArray(result, addressArray);
}

@JRubyMethod
@@ -146,55 +137,70 @@ public IRubyObject getpeereid(ThreadContext context) {

@Override
protected IRubyObject getSocknameCommon(ThreadContext context, String caller) {
try {
InetSocketAddress sock = getSocketAddress();
InetSocketAddress sock = getInetSocketAddress();

return Sockaddr.packSockaddrFromAddress(context, sock);

} catch (BadDescriptorException e) {
throw context.runtime.newErrnoEBADFError();
}
return Sockaddr.packSockaddrFromAddress(context, sock);
}

@Override
public IRubyObject getpeername(ThreadContext context) {
try {
InetSocketAddress sock = getRemoteSocket();
InetSocketAddress sock = getInetRemoteSocket();

return Sockaddr.packSockaddrFromAddress(context, sock);
return Sockaddr.packSockaddrFromAddress(context, sock);
}

} catch (BadDescriptorException e) {
throw context.runtime.newErrnoEBADFError();
}
private IRubyObject addrCommon(ThreadContext context, IRubyObject reverse) {
Boolean doReverse = doReverseLookup(context, reverse);
if (doReverse == null) doReverse = false;

return addrCommon(context, doReverse);
}

private IRubyObject addrCommon(ThreadContext context, boolean reverse) {
try {
InetSocketAddress address = getSocketAddress();
InetSocketAddress address = getInetSocketAddress();

if (address == null) {
throw context.runtime.newErrnoENOTSOCKError("Not socket or not connected");
}
checkAddress(context, address);

return addrFor(context, address, reverse);
return addrFor(context, address, reverse);
}

} catch (BadDescriptorException e) {
throw context.runtime.newErrnoEBADFError();
private void checkAddress(ThreadContext context, InetSocketAddress address) {
if (address == null) {
throw context.runtime.newErrnoENOTSOCKError("Not socket or not connected");
}
}

private IRubyObject peeraddrCommon(ThreadContext context, IRubyObject reverse) {
Boolean doReverse = doReverseLookup(context, reverse);
if (doReverse == null) doReverse = !context.runtime.isDoNotReverseLookupEnabled();

return peeraddrCommon(context, doReverse);
}

private IRubyObject peeraddrCommon(ThreadContext context, boolean reverse) {
try {
InetSocketAddress address = getRemoteSocket();
InetSocketAddress address = getInetRemoteSocket();

if (address == null) {
throw context.runtime.newErrnoENOTSOCKError("Not socket or not connected");
}
checkAddress(context, address);

return addrFor(context, address, reverse);
return addrFor(context, address, reverse);
}

} catch (BadDescriptorException e) {
throw context.runtime.newErrnoEBADFError();
public static Boolean doReverseLookup(ThreadContext context, IRubyObject noreverse) {
Ruby runtime = context.runtime;

if (noreverse == runtime.getTrue()) {
return false;
} else if (noreverse == runtime.getFalse()) {
return true;
} else if (noreverse == context.nil) {
return null;
} else {
TypeConverter.checkType(context, noreverse, runtime.getSymbol());
switch (noreverse.toString()) {
case "numeric": return true;
case "hostname": return false;
default: throw runtime.newArgumentError("invalid reverse_lookup flag: " + noreverse);
}
}
}

27 changes: 19 additions & 8 deletions core/src/main/java/org/jruby/ext/socket/RubyServerSocket.java
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.StandardSocketOptions;
import java.net.UnknownHostException;
import java.nio.channels.Channel;
import java.nio.channels.IllegalBlockingModeException;
@@ -100,6 +101,9 @@ public IRubyObject bind(ThreadContext context, IRubyObject addr) {

if (addr instanceof Addrinfo) {
Addrinfo addrInfo = (Addrinfo) addr;
if (!addrInfo.ip_p(context).isTrue()) {
throw context.runtime.newTypeError("not an INET or INET6 address: " + addrInfo);
}
iaddr = new InetSocketAddress(addrInfo.getInetAddress().getHostAddress(), addrInfo.getPort());
} else {
iaddr = Sockaddr.addressFromSockaddr_in(context, addr);
@@ -115,6 +119,9 @@ public IRubyObject bind(ThreadContext context, IRubyObject addr, IRubyObject bac

if (addr instanceof Addrinfo) {
Addrinfo addrInfo = (Addrinfo) addr;
if (!addrInfo.ip_p(context).isTrue()) {
throw context.runtime.newTypeError("not an INET or INET6 address: " + addrInfo);
}
iaddr = new InetSocketAddress(addrInfo.getInetAddress().getHostAddress(), addrInfo.getPort());
} else {
iaddr = Sockaddr.addressFromSockaddr_in(context, addr);
@@ -126,7 +133,7 @@ public IRubyObject bind(ThreadContext context, IRubyObject addr, IRubyObject bac

@JRubyMethod()
public IRubyObject accept(ThreadContext context) {
return doAccept(context, getChannel(), true);
return doAccept(this, context, true);
}

@JRubyMethod()
@@ -136,9 +143,10 @@ public IRubyObject accept_nonblock(ThreadContext context) {

@JRubyMethod()
public IRubyObject accept_nonblock(ThreadContext context, IRubyObject opts) {
return doAcceptNonblock(context, getChannel(), ArgsUtil.extractKeywordArg(context, "exception", opts) != context.runtime.getFalse());
return doAcceptNonblock(this, context, ArgsUtil.extractKeywordArg(context, "exception", opts) != context.runtime.getFalse());
}

@Override
protected ChannelFD initChannelFD(Ruby runtime) {
Channel channel;

@@ -157,8 +165,9 @@ protected ChannelFD initChannelFD(Ruby runtime) {
}
}

private IRubyObject doAcceptNonblock(ThreadContext context, Channel channel, boolean ex) {
public static IRubyObject doAcceptNonblock(RubySocket sock, ThreadContext context, boolean ex) {
try {
Channel channel = sock.getChannel();
if (channel instanceof SelectableChannel) {
SelectableChannel selectable = (SelectableChannel)channel;

@@ -168,7 +177,7 @@ private IRubyObject doAcceptNonblock(ThreadContext context, Channel channel, boo
try {
selectable.configureBlocking(false);

IRubyObject socket = doAccept(context, channel, ex);
IRubyObject socket = doAccept(sock, context, ex);
if (!(socket instanceof RubySocket)) return socket;
SocketChannel socketChannel = (SocketChannel)((RubySocket)socket).getChannel();
InetSocketAddress addr = (InetSocketAddress)socketChannel.socket().getRemoteSocketAddress();
@@ -190,12 +199,14 @@ private IRubyObject doAcceptNonblock(ThreadContext context, Channel channel, boo
}
}

private IRubyObject doAccept(ThreadContext context, Channel channel, boolean ex) {
public static IRubyObject doAccept(RubySocket sock, ThreadContext context, boolean ex) {
Ruby runtime = context.runtime;

Channel channel = sock.getChannel();

try {
if (channel instanceof ServerSocketChannel) {
ServerSocketChannel serverChannel = (ServerSocketChannel)getChannel();
ServerSocketChannel serverChannel = (ServerSocketChannel)sock.getChannel();

SocketChannel socket = serverChannel.accept();

@@ -208,9 +219,9 @@ private IRubyObject doAccept(ThreadContext context, Channel channel, boolean ex)
}

RubySocket rubySocket = new RubySocket(runtime, runtime.getClass("Socket"));
rubySocket.initFromServer(runtime, this, socket);
rubySocket.initFromServer(runtime, sock, socket);

return rubySocket;
return runtime.newArray(rubySocket, new Addrinfo(runtime, runtime.getClass("Addrinfo"), socket.getRemoteAddress()));
}
throw runtime.newErrnoENOPROTOOPTError();
}
152 changes: 103 additions & 49 deletions core/src/main/java/org/jruby/ext/socket/RubySocket.java
Original file line number Diff line number Diff line change
@@ -32,16 +32,20 @@
import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.PortUnreachableException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.Channel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ConnectionPendingException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Enumeration;
import java.util.regex.Pattern;
@@ -57,14 +61,18 @@
import jnr.constants.platform.SocketOption;
import jnr.constants.platform.TCP;
import jnr.netdb.Protocol;
import jnr.unixsocket.UnixServerSocketChannel;
import jnr.unixsocket.UnixSocketAddress;
import jnr.unixsocket.UnixSocketChannel;

import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.RubyString;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.ast.util.ArgsUtil;
@@ -73,6 +81,7 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.io.ChannelFD;
import org.jruby.util.io.Sockaddr;

@@ -111,8 +120,17 @@ static void createSocket(Ruby runtime) {
rb_mConstants.setConstant("MSG_DONTROUTE", runtime.newFixnum(MSG_DONTROUTE));
rb_mConstants.setConstant("MSG_WAITALL", runtime.newFixnum(MSG_WAITALL));

// constants webrick crashes without
rb_mConstants.setConstant("AI_PASSIVE", runtime.newFixnum(1));
rb_mConstants.setConstant("AI_CANONNAME", runtime.newFixnum(2));
rb_mConstants.setConstant("AI_NUMERICHOST", runtime.newFixnum(4));
rb_mConstants.setConstant("AI_ALL", runtime.newFixnum(256));
rb_mConstants.setConstant("AI_V4MAPPED_CFG", runtime.newFixnum(512));
rb_mConstants.setConstant("AI_ADDRCONFIG", runtime.newFixnum(1024));
rb_mConstants.setConstant("AI_V4MAPPED", runtime.newFixnum(2048));
rb_mConstants.setConstant("AI_NUMERICSERV", runtime.newFixnum(4096));

rb_mConstants.setConstant("AI_DEFAULT", runtime.newFixnum(1536));
rb_mConstants.setConstant("AI_MASK", runtime.newFixnum(5127));

// More constants needed by specs
rb_mConstants.setConstant("IP_MULTICAST_TTL", runtime.newFixnum(10));
@@ -167,11 +185,7 @@ public static IRubyObject for_fd(ThreadContext context, IRubyObject socketClass,
public IRubyObject initialize(ThreadContext context, IRubyObject domain, IRubyObject type) {
Ruby runtime = context.runtime;

initFieldsFromArgs(runtime, domain, type);

ChannelFD fd = initChannelFD(runtime);

initSocket(fd);
initFromArgs(runtime, domain, type);

return this;
}
@@ -180,11 +194,7 @@ public IRubyObject initialize(ThreadContext context, IRubyObject domain, IRubyOb
public IRubyObject initialize(ThreadContext context, IRubyObject domain, IRubyObject type, IRubyObject protocol) {
Ruby runtime = context.runtime;

initFieldsFromArgs(runtime, domain, type, protocol);

ChannelFD fd = initChannelFD(runtime);

initSocket(fd);
initFromArgs(runtime, domain, type, protocol);

return this;
}
@@ -202,14 +212,14 @@ public IRubyObject connect_nonblock(ThreadContext context, IRubyObject arg, IRub

boolean exception = ArgsUtil.extractKeywordArg(context, "exception", opts) != runtime.getFalse();

return doConnectNonblock(context, getChannel(), addr, exception);
return doConnectNonblock(context, addr, exception);
}

@JRubyMethod()
public IRubyObject connect(ThreadContext context, IRubyObject arg) {
SocketAddress addr = addressForChannel(context, arg);

return doConnect(context, getChannel(), addr, true);
return doConnect(context, addr, true);
}

@JRubyMethod()
@@ -218,35 +228,40 @@ public IRubyObject bind(ThreadContext context, IRubyObject arg) {

if (arg instanceof Addrinfo) {
Addrinfo addr = (Addrinfo) arg;
if (!addr.ip_p(context).isTrue()) {
throw context.runtime.newTypeError("not an INET or INET6 address: " + addr);
}
iaddr = new InetSocketAddress(addr.getInetAddress().getHostAddress(), addr.getPort());
}
else {
iaddr = Sockaddr.addressFromSockaddr_in(context, arg);
}

doBind(context, getChannel(), iaddr);
doBind(context, iaddr);

return RubyFixnum.zero(context.runtime);
}

@JRubyMethod
public IRubyObject recvfrom(ThreadContext context, IRubyObject length) {
return super.recv(context, length);
}

@JRubyMethod
public IRubyObject recvfrom(ThreadContext context, IRubyObject length, IRubyObject flags) {
return super.recv(context, length, flags);
public IRubyObject recvfrom(ThreadContext context, IRubyObject _length) {
return RubyUDPSocket.recvfrom(this, context, _length);
}

/**
* Overrides IPSocket#recvfrom
*/
@JRubyMethod
public IRubyObject recvfrom_nonblock(ThreadContext context, IRubyObject length) {
return super.recv_nonblock(context, length);
public IRubyObject recvfrom(ThreadContext context, IRubyObject _length, IRubyObject _flags) {
// TODO: handle flags
return recvfrom(context, _length);
}

@JRubyMethod(required = 1, optional = 3)
public IRubyObject recvfrom_nonblock(ThreadContext context, IRubyObject[] args) {
return super.recv_nonblock(context, args);
if (getOpenFile() == null) {
throw context.runtime.newErrnoENOTCONNError("socket is not connected");
}
return RubyUDPSocket.recvfrom_nonblock(this, context, args);
}

@JRubyMethod(notImplemented = true)
@@ -303,7 +318,7 @@ public static IRubyObject getservbyname(ThreadContext context, IRubyObject recv,

@JRubyMethod(name = {"pack_sockaddr_in", "sockaddr_in"}, meta = true)
public static IRubyObject pack_sockaddr_in(ThreadContext context, IRubyObject recv, IRubyObject port, IRubyObject host) {
return SocketUtils.pack_sockaddr_in(context, port, host);
return Sockaddr.pack_sockaddr_in(context, port, host);
}

@JRubyMethod(meta = true)
@@ -313,7 +328,13 @@ public static IRubyObject unpack_sockaddr_in(ThreadContext context, IRubyObject

@JRubyMethod(name = {"pack_sockaddr_un", "sockaddr_un"}, meta = true)
public static IRubyObject pack_sockaddr_un(ThreadContext context, IRubyObject recv, IRubyObject filename) {
return SocketUtils.pack_sockaddr_un(context, filename);
String path = filename.convertToString().asJavaString();
return Sockaddr.pack_sockaddr_un(context, path);
}

@JRubyMethod(meta = true)
public static IRubyObject unpack_sockaddr_un(ThreadContext context, IRubyObject recv, IRubyObject addr) {
return Sockaddr.unpack_sockaddr_un(context, addr);
}

@JRubyMethod(meta = true)
@@ -362,11 +383,6 @@ public static IRubyObject socketpair(ThreadContext context, IRubyObject recv, IR
return RubyUNIXSocket.socketpair(context, recv, arrayOf(domain, type));
}

@Override
protected Sock getDefaultSocketType() {
return soType;
}

private void initFieldsFromDescriptor(Ruby runtime, ChannelFD fd) {
Channel mainChannel = fd.ch;

@@ -395,21 +411,20 @@ private void initFieldsFromDescriptor(Ruby runtime, ChannelFD fd) {
}
}

private void initFieldsFromArgs(Ruby runtime, IRubyObject domain, IRubyObject type, IRubyObject protocol) {
initDomain(runtime, domain);

initType(runtime, type);

initProtocol(protocol);
private void initFromArgs(Ruby runtime, IRubyObject domain, IRubyObject type, IRubyObject protocol) {
setProtocol(protocol);
initFromArgs(runtime, domain, type);
}

private void initFieldsFromArgs(Ruby runtime, IRubyObject domain, IRubyObject type) {
initDomain(runtime, domain);
private void initFromArgs(Ruby runtime, IRubyObject domain, IRubyObject type) {
setDomain(runtime, domain);
setType(runtime, type);

initType(runtime, type);
ChannelFD fd = initChannelFD(runtime);
initSocket(fd);
}

protected void initFromServer(Ruby runtime, RubyServerSocket serverSocket, SocketChannel socketChannel) {
protected void initFromServer(Ruby runtime, RubySocket serverSocket, SocketChannel socketChannel) {
soDomain = serverSocket.soDomain;
soType = serverSocket.soType;
soProtocol = serverSocket.soProtocol;
@@ -441,18 +456,19 @@ else if ( soProtocolFamily == ProtocolFamily.PF_INET ||
default:
throw runtime.newArgumentError("unsupported socket type `" + soType + "'");
}

return newChannelFD(runtime, channel);
}
catch (IOException e) {
throw sockerr(runtime, "initialize: " + e.toString(), e);
}
}

private void initProtocol(IRubyObject protocol) {
private void setProtocol(IRubyObject protocol) {
soProtocol = SocketUtils.protocolFromArg(protocol);
}

private void initType(Ruby runtime, IRubyObject type) {
private void setType(Ruby runtime, IRubyObject type) {
Sock sockType = SocketUtils.sockFromArg(type);

if (sockType == null) {
@@ -462,7 +478,7 @@ private void initType(Ruby runtime, IRubyObject type) {
soType = sockType;
}

private void initDomain(Ruby runtime, IRubyObject domain) {
private void setDomain(Ruby runtime, IRubyObject domain) {
AddressFamily family = SocketUtils.addressFamilyFromArg(domain);

if (family == null) {
@@ -475,7 +491,9 @@ private void initDomain(Ruby runtime, IRubyObject domain) {
soProtocolFamily = ProtocolFamily.valueOf("PF" + name.substring(2));
}

private IRubyObject doConnectNonblock(ThreadContext context, Channel channel, SocketAddress addr, boolean ex) {
private IRubyObject doConnectNonblock(ThreadContext context, SocketAddress addr, boolean ex) {
Channel channel = getChannel();

if ( ! (channel instanceof SelectableChannel) ) {
throw context.runtime.newErrnoENOPROTOOPTError();
}
@@ -487,7 +505,7 @@ private IRubyObject doConnectNonblock(ThreadContext context, Channel channel, So
selectable.configureBlocking(false);

try {
return doConnect(context, channel, addr, ex);
return doConnect(context, addr, ex);

} finally {
selectable.configureBlocking(oldBlocking);
@@ -503,8 +521,9 @@ private IRubyObject doConnectNonblock(ThreadContext context, Channel channel, So
}
}

protected IRubyObject doConnect(ThreadContext context, Channel channel, SocketAddress addr, boolean ex) {
protected IRubyObject doConnect(ThreadContext context, SocketAddress addr, boolean ex) {
Ruby runtime = context.runtime;
Channel channel = getChannel();

try {
boolean result = true;
@@ -557,9 +576,11 @@ else if (channel instanceof DatagramChannel) {
return runtime.newFixnum(0);
}

protected void doBind(ThreadContext context, Channel channel, InetSocketAddress iaddr) {
protected void doBind(ThreadContext context, SocketAddress iaddr) {
Ruby runtime = context.runtime;

Channel channel = getChannel();

try {
if (channel instanceof SocketChannel) {
Socket socket = ((SocketChannel) channel).socket();
@@ -630,7 +651,7 @@ private static CharSequence formatAddress(final SocketAddress addr) {
}

private SocketAddress addressForChannel(ThreadContext context, IRubyObject arg) {
if (arg instanceof Addrinfo) return Sockaddr.addressFromArg(context, arg);
if (arg instanceof Addrinfo) return ((Addrinfo) arg).getSocketAddress();

switch (soProtocolFamily) {
case PF_UNIX:
@@ -647,6 +668,39 @@ private SocketAddress addressForChannel(ThreadContext context, IRubyObject arg)
}
}

@Override
protected IRubyObject addrFor(ThreadContext context, InetSocketAddress addr, boolean reverse) {
final Ruby runtime = context.runtime;

return new Addrinfo(runtime, runtime.getClass("Addrinfo"), addr.getAddress(), addr.getPort(), Sock.SOCK_DGRAM);
}

@JRubyMethod
public IRubyObject close() {
if (getOpenFile() != null) {
Ruby runtime = getRuntime();
if (isClosed()) {
return runtime.getNil();
}
openFile.checkClosed();
return rbIoClose(runtime);
}
return getRuntime().getNil();
}

@Override
public RubyBoolean closed_p(ThreadContext context) {
if (getOpenFile() == null) return context.runtime.getFalse();

return super.closed_p(context);
}

protected SocketAddress getSocketAddress() {
Channel channel = getChannel();

return SocketType.forChannel(channel).getLocalSocketAddress(channel);
}

@Deprecated
public static RuntimeException sockerr(Ruby runtime, String msg) {
return SocketUtils.sockerr(runtime, msg);
33 changes: 21 additions & 12 deletions core/src/main/java/org/jruby/ext/socket/RubyTCPServer.java
Original file line number Diff line number Diff line change
@@ -82,18 +82,27 @@ public RubyTCPServer(Ruby runtime, RubyClass type) {
@JRubyMethod(name = "initialize", required = 1, optional = 1, visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
IRubyObject _host = args[0];
IRubyObject _port = args.length > 1 ? args[1] : context.nil;

String host;
if(_host.isNil()|| ((_host instanceof RubyString) && ((RubyString) _host).isEmpty())) {
host = "0.0.0.0";
} else if (_host instanceof RubyFixnum) {
// numeric host, use it for port
_port = _host;
host = "0.0.0.0";
} else {
host = _host.convertToString().toString();
IRubyObject _host;
IRubyObject _port = null;
String host = "0.0.0.0";

switch (args.length) {
case 2:
_host = args[0];
_port = args[1];

if (_host.isNil()) {
break;
} else if (_host instanceof RubyFixnum) {
throw runtime.newTypeError(_host, runtime.getString());
}

RubyString hostString = _host.convertToString();
if (hostString.size() > 0) host = hostString.toString();

break;
case 1:
_port = args[0];
}

int port = SocketUtils.getPortFrom(context, _port);
114 changes: 65 additions & 49 deletions core/src/main/java/org/jruby/ext/socket/RubyUDPSocket.java
Original file line number Diff line number Diff line change
@@ -33,9 +33,11 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.PortUnreachableException;
import java.net.ProtocolFamily;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.MulticastSocket;
import java.net.StandardProtocolFamily;
import java.net.UnknownHostException;
import java.net.DatagramPacket;
import java.nio.ByteBuffer;
@@ -44,10 +46,12 @@
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.NotYetConnectedException;

import jnr.constants.platform.AddressFamily;
import jnr.netdb.Service;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
import org.jruby.RubyInteger;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.RubyString;
@@ -92,31 +96,37 @@ public RubyUDPSocket(Ruby runtime, RubyClass type) {
@Override
@JRubyMethod(visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context) {
return initialize(context, StandardProtocolFamily.INET);
}

@JRubyMethod(visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject _family) {
int family = _family.convertToInteger().getIntValue();
if (family == AddressFamily.AF_INET.intValue()) {
return initialize(context, StandardProtocolFamily.INET);
} else if (family == AddressFamily.AF_INET6.intValue()) {
return initialize(context, StandardProtocolFamily.INET6);
}
throw SocketUtils.sockerr(context.runtime, "invalid family for UDPSocket: " + _family);
}

public IRubyObject initialize(ThreadContext context, ProtocolFamily family) {
Ruby runtime = context.runtime;

try {
DatagramChannel channel = DatagramChannel.open();
DatagramChannel channel = DatagramChannel.open(family);
initSocket(newChannelFD(runtime, channel));
}
catch (ConnectException e) {
} catch (ConnectException e) {
throw runtime.newErrnoECONNREFUSEDError();
}
catch (UnknownHostException e) {
} catch (UnknownHostException e) {
throw SocketUtils.sockerr(runtime, "initialize: name or service not known");
}
catch (IOException e) {
} catch (IOException e) {
throw sockerr(runtime, "initialize: name or service not known", e);
}

return this;
}

@JRubyMethod(visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject protocol) {
// we basically ignore protocol. let someone report it...
return initialize(context);
}

@JRubyMethod
public IRubyObject bind(ThreadContext context, IRubyObject host, IRubyObject _port) {
final Ruby runtime = context.runtime;
@@ -133,8 +143,15 @@ public IRubyObject bind(ThreadContext context, IRubyObject host, IRubyObject _po
}
else if (host instanceof RubyFixnum) {
// passing in something like INADDR_ANY
final int intAddr = RubyNumeric.fix2int(host);
final RubyModule Socket = runtime.getModule("Socket");
int intAddr = 0;
if (host instanceof RubyInteger) {
intAddr = RubyNumeric.fix2int(host);
} else if (host instanceof RubyString) {
intAddr = ((RubyString)host).to_i().convertToInteger().getIntValue();
} else {
throw runtime.newTypeError(host, runtime.getInteger());
}
RubyModule Socket = runtime.getModule("Socket");
if (intAddr == RubyNumeric.fix2int(Socket.getConstant("INADDR_ANY"))) {
addr = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), port);
}
@@ -150,7 +167,7 @@ else if (host instanceof RubyFixnum) {
}

if (multicastStateManager == null) {
((DatagramChannel) channel).socket().bind(addr);
((DatagramChannel) channel).bind(addr);
} else {
multicastStateManager.rebindToPort(port);
}
@@ -200,13 +217,16 @@ public IRubyObject connect(ThreadContext context, IRubyObject host, IRubyObject
}
}

@JRubyMethod
public IRubyObject recvfrom_nonblock(ThreadContext context, IRubyObject length) {
return recv_nonblock(context, length, context.nil, /* str */ null, true);
private DatagramChannel getDatagramChannel() {
return (DatagramChannel) getChannel();
}

@JRubyMethod(required = 1, optional = 3) // (length) required = 1 handled above
@JRubyMethod(required = 1, optional = 3)
public IRubyObject recvfrom_nonblock(ThreadContext context, IRubyObject[] args) {
return recvfrom_nonblock(this, context, args);
}

public static IRubyObject recvfrom_nonblock(RubyBasicSocket socket, ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
int argc = args.length;
IRubyObject opts = ArgsUtil.getOptionsArg(context.runtime, args);
@@ -223,15 +243,15 @@ public IRubyObject recvfrom_nonblock(ThreadContext context, IRubyObject[] args)

boolean exception = ArgsUtil.extractKeywordArg(context, "exception", opts) != runtime.getFalse();

return recvfrom_nonblock(context, length, flags, str, exception);
return recvfrom_nonblock(socket, context, length, flags, str, exception);
}

private IRubyObject recvfrom_nonblock(ThreadContext context,
private static IRubyObject recvfrom_nonblock(RubyBasicSocket socket, ThreadContext context,
IRubyObject length, IRubyObject flags, IRubyObject str, boolean exception) {
final Ruby runtime = context.runtime;

try {
ReceiveTuple tuple = doReceiveNonblockTuple(runtime, RubyNumeric.fix2int(length));
ReceiveTuple tuple = doReceiveNonblockTuple(socket, runtime, RubyNumeric.fix2int(length));

if (tuple == null) {
if (!exception) return context.runtime.newSymbol("wait_readable");
@@ -247,7 +267,7 @@ private IRubyObject recvfrom_nonblock(ThreadContext context,
str = tuple.result;
}

IRubyObject addressArray = addrFor(context, tuple.sender, false);
IRubyObject addressArray = socket.addrFor(context, tuple.sender, false);

return runtime.newArray(str, addressArray);
}
@@ -395,12 +415,16 @@ public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObj
*/
@Override
public IRubyObject recvfrom(ThreadContext context, IRubyObject length) {
return recvfrom(this, context, length);
}

public static IRubyObject recvfrom(RubyBasicSocket socket, ThreadContext context, IRubyObject length) {
final Ruby runtime = context.runtime;

try {
ReceiveTuple tuple = doReceiveTuple(runtime, false, RubyNumeric.fix2int(length));
ReceiveTuple tuple = doReceiveTuple(socket, runtime, false, RubyNumeric.fix2int(length));

IRubyObject addressArray = addrFor(context, tuple.sender, false);
IRubyObject addressArray = socket.addrFor(context, tuple.sender, false);

return runtime.newArray(tuple.result, addressArray);
}
@@ -436,7 +460,7 @@ public IRubyObject recv(ThreadContext context, IRubyObject length) {
final Ruby runtime = context.runtime;

try {
return doReceive(runtime, false, RubyNumeric.fix2int(length), null);
return doReceive(this, runtime, false, RubyNumeric.fix2int(length), null);
}
catch (IOException e) { // SocketException
throw runtime.newIOErrorFromException(e);
@@ -456,50 +480,43 @@ public IRubyObject recv(ThreadContext context, IRubyObject _length, IRubyObject
return recv(context, _length);
}

private ReceiveTuple doReceiveTuple(final Ruby runtime, final boolean non_block, int length) throws IOException {
private static ReceiveTuple doReceiveTuple(RubyBasicSocket socket, final Ruby runtime, final boolean non_block, int length) throws IOException {
ReceiveTuple tuple = new ReceiveTuple();

final IRubyObject result;
if (this.multicastStateManager == null) {
result = doReceive(runtime, non_block, length, tuple);
if (socket.multicastStateManager == null) {
result = doReceive(socket, runtime, non_block, length, tuple);
} else {
result = doReceiveMulticast(runtime, non_block, length, tuple);
result = doReceiveMulticast(socket, runtime, non_block, length, tuple);
}
return result == null ? null : tuple; // need to return null for non_block (if op would block)
}

private ReceiveTuple doReceiveNonblockTuple(Ruby runtime, int length) throws IOException {
DatagramChannel channel = (DatagramChannel) getChannel();
private static ReceiveTuple doReceiveNonblockTuple(RubyBasicSocket socket, Ruby runtime, int length) throws IOException {
DatagramChannel channel = (DatagramChannel) socket.getChannel();

synchronized (channel.blockingLock()) {
boolean oldBlocking = channel.isBlocking();

channel.configureBlocking(false);

try {
return doReceiveTuple(runtime, true, length);
return doReceiveTuple(socket, runtime, true, length);
}
finally {
channel.configureBlocking(oldBlocking);
}
}
}

private static final class ReceiveTuple {
ReceiveTuple() {}

//ReceiveTuple(RubyString result, InetSocketAddress sender) {
// this.result = result;
// this.sender = sender;
//}

RubyString result;
InetSocketAddress sender;
private static IRubyObject doReceive(RubyBasicSocket socket, final Ruby runtime, final boolean non_block,
int length) throws IOException {
return doReceive(socket, runtime, non_block, length, null);
}

private IRubyObject doReceive(final Ruby runtime, final boolean non_block,
protected static IRubyObject doReceive(RubyBasicSocket socket, final Ruby runtime, final boolean non_block,
int length, ReceiveTuple tuple) throws IOException {
DatagramChannel channel = (DatagramChannel) getChannel();
DatagramChannel channel = (DatagramChannel) socket.getChannel();

ByteBuffer buf = ByteBuffer.allocate(length);

@@ -524,12 +541,12 @@ private IRubyObject doReceive(final Ruby runtime, final boolean non_block,
return result;
}

private IRubyObject doReceiveMulticast(final Ruby runtime, final boolean non_block,
private static IRubyObject doReceiveMulticast(RubyBasicSocket socket, final Ruby runtime, final boolean non_block,
int length, ReceiveTuple tuple) throws IOException {
ByteBuffer recv = ByteBuffer.wrap(new byte[length]);
SocketAddress address;

DatagramChannel channel = this.multicastStateManager.getMulticastSocket().getChannel();
DatagramChannel channel = socket.multicastStateManager.getMulticastSocket().getChannel();

address = channel.receive(recv);

@@ -581,4 +598,3 @@ public static IRubyObject open(IRubyObject recv, IRubyObject[] args, Block block
return open(recv.getRuntime().getCurrentContext(), recv, args, block);
}
}// RubyUDPSocket

19 changes: 18 additions & 1 deletion core/src/main/java/org/jruby/ext/socket/RubyUNIXServer.java
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@


import jnr.unixsocket.UnixServerSocketChannel;
import jnr.unixsocket.UnixSocketAddress;
import jnr.unixsocket.UnixSocketChannel;
import org.jruby.Ruby;
import org.jruby.RubyClass;
@@ -42,6 +43,7 @@
import org.jruby.runtime.builtin.IRubyObject;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;

@@ -156,7 +158,8 @@ public IRubyObject listen(ThreadContext context, IRubyObject log) {

@JRubyMethod
public IRubyObject sysaccept(ThreadContext context) {
return accept(context);
RubyUNIXSocket socket = (RubyUNIXSocket) accept(context);
return context.runtime.newFixnum(((UnixSocketChannel) socket.getChannel()).getFD());
}

@JRubyMethod
@@ -178,6 +181,20 @@ public IRubyObject peeraddr(ThreadContext context) {
throw context.runtime.newErrnoENOTCONNError();
}

@Override
protected UnixSocketAddress getUnixSocketAddress() {
SocketAddress socketAddress = ((UnixServerSocketChannel)getChannel()).getLocalSocketAddress();
if (socketAddress instanceof UnixSocketAddress) return (UnixSocketAddress) socketAddress;
return null;
}

@Override
protected UnixSocketAddress getUnixRemoteSocket() {
SocketAddress socketAddress = ((UnixServerSocketChannel)getChannel()).getLocalSocketAddress();
if (socketAddress instanceof UnixSocketAddress) return (UnixSocketAddress) socketAddress;
return null;
}

private UnixServerSocketChannel asUnixServer() {
return (UnixServerSocketChannel)getChannel();
}
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/ext/socket/RubyUNIXSocket.java
Original file line number Diff line number Diff line change
@@ -53,12 +53,14 @@
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.io.BadDescriptorException;
import org.jruby.util.io.ModeFlags;
import org.jruby.util.io.OpenFile;
import org.jruby.util.io.FilenoUtil;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.Channel;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
23 changes: 16 additions & 7 deletions core/src/main/java/org/jruby/ext/socket/SocketType.java
Original file line number Diff line number Diff line change
@@ -29,8 +29,10 @@
import jnr.constants.platform.Sock;
import jnr.constants.platform.SocketOption;
import jnr.unixsocket.UnixServerSocketChannel;
import jnr.unixsocket.UnixSocketAddress;
import jnr.unixsocket.UnixSocketChannel;

import java.io.File;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
@@ -177,7 +179,7 @@ public SocketAddress getLocalSocketAddress(Channel channel) {
private DatagramSocket toSocket(Channel channel) {
return ((DatagramChannel)channel).socket();
}

public int getSoTimeout(Channel channel) throws IOException {
return toSocket(channel).getSoTimeout();
}
@@ -238,17 +240,25 @@ public void shutdownInput(Channel channel)throws IOException {
public void shutdownOutput(Channel channel) throws IOException {
toSocket(channel).shutdownOutput();
}

public SocketAddress getRemoteSocketAddress(Channel channel) {
return toSocket(channel).getRemoteSocketAddress();
}

public SocketAddress getLocalSocketAddress(Channel channel) {
return new UnixSocketAddress(new File("empty-path"));
}
},

UNKNOWN(Sock.SOCK_STREAM);

public static SocketType forChannel(Channel channel) {
if (channel instanceof SocketChannel) {
return SOCKET;

} else if (channel instanceof ServerSocketChannel) {
return SERVER;

} else if (channel instanceof DatagramChannel) {
return DATAGRAM;

@@ -301,7 +311,7 @@ public SocketAddress getLocalSocketAddress(Channel channel) {
public Sock getSocketType() {
return sock;
}

public int getSocketOption(Channel channel, SocketOption option) throws IOException {
switch (option) {

@@ -312,8 +322,7 @@ public int getSocketOption(Channel channel, SocketOption option) throws IOExcept
return getKeepAlive(channel) ? 1 : 0;

case SO_LINGER: {
int linger = getSoLinger(channel);
return linger < 0 ? 0 : linger;
return getSoLinger(channel);
}

case SO_OOBINLINE:
298 changes: 157 additions & 141 deletions core/src/main/java/org/jruby/ext/socket/SocketUtils.java

Large diffs are not rendered by default.

32 changes: 18 additions & 14 deletions core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -362,7 +362,7 @@ static MethodHandle buildGenericHandle(InvokeSite site, DynamicMethod method, Ru
}

static MethodHandle buildAttrHandle(InvokeSite site, DynamicMethod method, IRubyObject self, RubyClass dispatchClass) {
if (method instanceof AttrReaderMethod) {
if (method instanceof AttrReaderMethod && site.arity == 0) {
AttrReaderMethod attrReader = (AttrReaderMethod) method;
String varName = attrReader.getVariableName();

@@ -371,29 +371,29 @@ static MethodHandle buildAttrHandle(InvokeSite site, DynamicMethod method, IRuby

// Ruby to attr reader
if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
// if (accessor instanceof FieldVariableAccessor) {
// LOG.info(site.name() + "\tbound as field attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
// } else {
// LOG.info(site.name() + "\tbound as attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
// }
if (accessor instanceof FieldVariableAccessor) {
LOG.info(site.name() + "\tbound as field attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
} else {
LOG.info(site.name() + "\tbound as attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
}
}

return createAttrReaderHandle(site, self, dispatchClass.getRealClass(), accessor);
} else if (method instanceof AttrWriterMethod) {
} else if (method instanceof AttrWriterMethod && site.arity == 1) {
AttrWriterMethod attrReader = (AttrWriterMethod)method;
String varName = attrReader.getVariableName();

// we getVariableAccessorForWrite here so it is eagerly created and we don't cache the DUMMY
VariableAccessor accessor = dispatchClass.getRealClass().getVariableAccessorForWrite(varName);

// Ruby to attr reader
// if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
// if (accessor instanceof FieldVariableAccessor) {
// LOG.info(site.name() + "\tbound as field attr writer " + logMethod(method) + ":" + ((AttrWriterMethod) method).getVariableName());
// } else {
// LOG.info(site.name() + "\tbound as attr writer " + logMethod(method) + ":" + ((AttrWriterMethod) method).getVariableName());
// }
// }
if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
if (accessor instanceof FieldVariableAccessor) {
LOG.info(site.name() + "\tbound as field attr writer " + logMethod(method) + ":" + ((AttrWriterMethod) method).getVariableName());
} else {
LOG.info(site.name() + "\tbound as attr writer " + logMethod(method) + ":" + ((AttrWriterMethod) method).getVariableName());
}
}

return createAttrWriterHandle(site, self, dispatchClass.getRealClass(), accessor);
}
@@ -963,6 +963,10 @@ public static CallSite prepareBlock(Lookup lookup, String name, MethodType type,
return new ConstantCallSite(blockMaker);
}

private static String logMethod(DynamicMethod method) {
return "[#" + method.getSerialNumber() + " " + method.getImplementationClass() + "]";
}

private static final Binder BINDING_MAKER_BINDER = Binder.from(Binding.class, ThreadContext.class, IRubyObject.class, DynamicScope.class);

private static final MethodHandle FRAME_SCOPE_BINDING = BINDING_MAKER_BINDER.invokeStaticQuiet(LOOKUP, Bootstrap.class, "frameScopeBinding");
4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/InvokeSite.java
Original file line number Diff line number Diff line change
@@ -339,6 +339,10 @@ public static boolean testClass(Object object, Class clazz) {
return object.getClass() == clazz;
}

public String toString() {
return "InvokeSite[name=" + name() + ",arity=" + arity + ",type=" + type() + ",file=" + file + ",line=" + line + "]";
}

private static final MethodHandle TEST_CLASS = Binder
.from(boolean.class, Object.class, Class.class)
.invokeStaticQuiet(lookup(), InvokeSite.class, "testClass");
70 changes: 29 additions & 41 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -1455,42 +1455,7 @@ public void LineNumberInstr(LineNumberInstr linenumberinstr) {

@Override
public void LoadLocalVarInstr(LoadLocalVarInstr loadlocalvarinstr) {
IRBytecodeAdapter m = jvmMethod();
jvmLoadLocal(DYNAMIC_SCOPE);
int depth = loadlocalvarinstr.getLocalVar().getScopeDepth();
int location = loadlocalvarinstr.getLocalVar().getLocation();
// TODO if we can avoid loading nil unnecessarily, it could be a big win
OUTER: switch (depth) {
case 0:
switch (location) {
case 0:
m.pushNil();
m.adapter.invokevirtual(p(DynamicScope.class), "getValueZeroDepthZeroOrNil", sig(IRubyObject.class, IRubyObject.class));
break OUTER;
case 1:
m.pushNil();
m.adapter.invokevirtual(p(DynamicScope.class), "getValueOneDepthZeroOrNil", sig(IRubyObject.class, IRubyObject.class));
break OUTER;
case 2:
m.pushNil();
m.adapter.invokevirtual(p(DynamicScope.class), "getValueTwoDepthZeroOrNil", sig(IRubyObject.class, IRubyObject.class));
break OUTER;
case 3:
m.pushNil();
m.adapter.invokevirtual(p(DynamicScope.class), "getValueThreeDepthZeroOrNil", sig(IRubyObject.class, IRubyObject.class));
break OUTER;
default:
m.adapter.pushInt(location);
m.pushNil();
m.adapter.invokevirtual(p(DynamicScope.class), "getValueDepthZeroOrNil", sig(IRubyObject.class, int.class, IRubyObject.class));
break OUTER;
}
default:
m.adapter.pushInt(location);
m.adapter.pushInt(depth);
m.pushNil();
m.adapter.invokevirtual(p(DynamicScope.class), "getValueOrNil", sig(IRubyObject.class, int.class, int.class, IRubyObject.class));
}
LocalVariable(loadlocalvarinstr.getLocalVar());
jvmStoreLocal(loadlocalvarinstr.getResult());
}

@@ -2397,12 +2362,35 @@ public void Hash(Hash hash) {

@Override
public void LocalVariable(LocalVariable localvariable) {
// CON FIXME: This isn't as efficient as it could be, but we should not see these in optimized JIT scopes
IRBytecodeAdapter m = jvmMethod();
jvmLoadLocal(DYNAMIC_SCOPE);
jvmAdapter().ldc(localvariable.getOffset());
jvmAdapter().ldc(localvariable.getScopeDepth());
jvmMethod().pushNil();
jvmAdapter().invokevirtual(p(DynamicScope.class), "getValueOrNil", sig(IRubyObject.class, int.class, int.class, IRubyObject.class));
int depth = localvariable.getScopeDepth();
int location = localvariable.getLocation();
OUTER: switch (depth) {
case 0:
switch (location) {
case 0:
m.adapter.invokevirtual(p(DynamicScope.class), "getValueZeroDepthZero", sig(IRubyObject.class));
break OUTER;
case 1:
m.adapter.invokevirtual(p(DynamicScope.class), "getValueOneDepthZero", sig(IRubyObject.class));
break OUTER;
case 2:
m.adapter.invokevirtual(p(DynamicScope.class), "getValueTwoDepthZero", sig(IRubyObject.class));
break OUTER;
case 3:
m.adapter.invokevirtual(p(DynamicScope.class), "getValueThreeDepthZero", sig(IRubyObject.class));
break OUTER;
default:
m.adapter.pushInt(location);
m.adapter.invokevirtual(p(DynamicScope.class), "getValueDepthZero", sig(IRubyObject.class, int.class));
break OUTER;
}
default:
m.adapter.pushInt(location);
m.adapter.pushInt(depth);
m.adapter.invokevirtual(p(DynamicScope.class), "getValue", sig(IRubyObject.class, int.class, int.class));
}
}

@Override
5 changes: 5 additions & 0 deletions core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java
Original file line number Diff line number Diff line change
@@ -66,6 +66,11 @@ private static IRubyObject becomeJava(final ThreadContext context, final RubyCla
klass.reifyWithAncestors(dumpDir, useChildLoader);

final Class<?> reifiedClass = klass.getReifiedClass();

if (reifiedClass == null) {
throw context.runtime.newTypeError("requested class " + klass.getName() + " was not reifiable");
}

generateFieldAccessors(context, klass, reifiedClass);
return asJavaClass(context.runtime, reifiedClass);
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/lexer/yacc/HeredocTerm.java
Original file line number Diff line number Diff line change
@@ -138,7 +138,7 @@ public int parseString(RubyLexer lexer) throws java.io.IOException {
lexer.lex_goto_eol();

if (lexer.getHeredocIndent() > 0) {
lexer.setValue(str);
lexer.setValue(lexer.createStr(str, 0));
return Tokens.tSTRING_CONTENT;
}
// MRI null checks str in this case but it is unconditionally non-null?
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/platform/Platform.java
Original file line number Diff line number Diff line change
@@ -82,6 +82,9 @@ private static String initOperatingSystem() {
if (osname.startsWith("windows")) {
return WINDOWS;
}
if (osname.startsWith("sunos")) {
return SOLARIS;
}
return osname;
}
private static String initArchitecture() {
28 changes: 25 additions & 3 deletions core/src/main/java/org/jruby/runtime/DynamicScope.java
Original file line number Diff line number Diff line change
@@ -160,15 +160,37 @@ public final String[] getAllNamesInScope() {
/**
* Get value from current scope or one of its captured scopes.
*
* FIXME: block variables are not getting primed to nil so we need to null check those
* until we prime them properly. Also add assert back in.
*
* @param offset zero-indexed value that represents where variable lives
* @param depth how many captured scopes down this variable should be set
* @return the value here
*/
public abstract IRubyObject getValue(int offset, int depth);

/**
* Variation of getValue for depth 0
*/
public abstract IRubyObject getValueDepthZero(int offset);

/**
* getValue for index 0, depth 0
*/
public abstract IRubyObject getValueZeroDepthZero();

/**
* getValue for index 1, depth 0
*/
public abstract IRubyObject getValueOneDepthZero();

/**
* getValue for index 2, depth 0
*/
public abstract IRubyObject getValueTwoDepthZero();

/**
* getValue for index 3, depth 0
*/
public abstract IRubyObject getValueThreeDepthZero();

/**
* Variation of getValue that checks for nulls, returning and setting the given value (presumably nil)
*/
6 changes: 6 additions & 0 deletions core/src/main/java/org/jruby/runtime/Helpers.java
Original file line number Diff line number Diff line change
@@ -2801,6 +2801,12 @@ public static String encodeParameterList(List<String[]> args) {
return builder.toString();
}

public static byte[] subseq(byte[] ary, int start, int len) {
byte[] newAry = new byte[len];
System.arraycopy(ary, start, newAry, 0, len);
return newAry;
}

/**
* This method is deprecated because it depends on having a Ruby frame pushed for checking method visibility,
* and there's no way to enforce that. Most users of this method probably don't need to check visibility.
42 changes: 24 additions & 18 deletions core/src/main/java/org/jruby/runtime/ThreadContext.java
Original file line number Diff line number Diff line change
@@ -145,29 +145,35 @@ public static ThreadContext newContext(Ruby runtime) {

public final JavaSites sites;

@SuppressWarnings("deprecated")
@SuppressWarnings("deprecation")
public SecureRandom getSecureRandom() {
SecureRandom secureRandom = this.secureRandom;
if (secureRandom == null) {
if (tryPreferredPRNG) {
try {
secureRandom = SecureRandom.getInstance(Options.PREFERRED_PRNG.load());
} catch (Exception e) {
tryPreferredPRNG = false;
}

// Try preferred PRNG, which defaults to NativePRNGNonBlocking
if (secureRandom == null && tryPreferredPRNG) {
try {
secureRandom = SecureRandom.getInstance(Options.PREFERRED_PRNG.load());
} catch (Exception e) {
tryPreferredPRNG = false;
}
if (secureRandom == null) {
if (trySHA1PRNG) {
try {
secureRandom = SecureRandom.getInstance("SHA1PRNG");
} catch (Exception e) {
trySHA1PRNG = false;
}
}
secureRandom = new SecureRandom();
}

// Try SHA1PRNG
if (secureRandom == null && trySHA1PRNG) {
try {
secureRandom = SecureRandom.getInstance("SHA1PRNG");
} catch (Exception e) {
trySHA1PRNG = false;
}
this.secureRandom = secureRandom;
}

// Just let JDK do whatever it does
if (secureRandom == null) {
secureRandom = new SecureRandom();
}

this.secureRandom = secureRandom;

return secureRandom;
}

Original file line number Diff line number Diff line change
@@ -30,20 +30,21 @@
import org.jruby.common.IRubyWarnings;
import org.jruby.common.RubyWarnings;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.util.collections.NonBlockingHashMapLong;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* This is a collection af all methods which will be profiled.
* Current it's just a wrapper for a {@link java.util.concurrent.ConcurrentMap},
* Current it's just a wrapper for a {@link NonBlockingHashMapLong},
* but the implementation can be changed in the future without changing the interface.
*
* @author Andre Kullmann
*/
public class ProfiledMethods {

private final ConcurrentMap<Long,ProfiledMethod> methods;
private final NonBlockingHashMapLong<ProfiledMethod> methods;

private final Ruby runtime;

@@ -54,7 +55,7 @@ public ProfiledMethods( final Ruby runtime ) {

this.runtime = runtime;
// TODO is 10000 a good value ?
this.methods = new ConcurrentHashMap<Long,ProfiledMethod>(10000);
this.methods = new NonBlockingHashMapLong<>(10000);
}

private Ruby getRuntime() {
Original file line number Diff line number Diff line change
@@ -78,6 +78,23 @@ public IRubyObject getValueOrNil(int offset, int depth, IRubyObject nil) {
return getValueDepthZeroOrNil(offset, nil);
}
}

@Override
public IRubyObject getValueDepthZero(int offset) {
assert offset < SIZE : SIZE_ERROR;
switch (offset) {
case 0:
return variableValueZero;
case 1:
return variableValueOne;
case 2:
return variableValueTwo;
case 3:
return variableValueThree;
default:
throw new RuntimeException(SIZE_ERROR);
}
}

@Override
public IRubyObject getValueDepthZeroOrNil(int offset, IRubyObject nil) {
@@ -99,6 +116,12 @@ public IRubyObject getValueDepthZeroOrNil(int offset, IRubyObject nil) {
throw new RuntimeException(SIZE_ERROR);
}
}

@Override
public IRubyObject getValueThreeDepthZero() {
return variableValueThree;
}

@Override
public IRubyObject getValueThreeDepthZeroOrNil(IRubyObject nil) {
if (variableValueThree == null) return variableValueThree = nil;
Original file line number Diff line number Diff line change
@@ -69,6 +69,26 @@ public IRubyObject getValue(int offset, int depth) {
//assert variableValues[offset] != null : "Getting unassigned: " + staticScope.getVariables()[offset];
return variableValues[offset];
}

public IRubyObject getValueDepthZero(int offset) {
return variableValues[offset];
}

public IRubyObject getValueZeroDepthZero() {
return variableValues[0];
}

public IRubyObject getValueOneDepthZero() {
return variableValues[1];
}

public IRubyObject getValueTwoDepthZero() {
return variableValues[2];
}

public IRubyObject getValueThreeDepthZero() {
return variableValues[3];
}

/**
* Variation of getValue that checks for nulls, returning and setting the given value (presumably nil)
16 changes: 16 additions & 0 deletions core/src/main/java/org/jruby/runtime/scope/NoVarsDynamicScope.java
Original file line number Diff line number Diff line change
@@ -52,6 +52,22 @@ public IRubyObject getValue(int offset, int depth) {
assert depth != 0: SIZE_ERROR;
return parent.getValue(offset, depth - 1);
}

public IRubyObject getValueDepthZero(int offset) {
throw new RuntimeException(this.getClass().getSimpleName() + " does not support scopes with any variables");
}
public IRubyObject getValueZeroDepthZero() {
throw new RuntimeException(this.getClass().getSimpleName() + " does not support scopes with one or more variables");
}
public IRubyObject getValueOneDepthZero() {
throw new RuntimeException(this.getClass().getSimpleName() + " does not support scopes with two or more variables");
}
public IRubyObject getValueTwoDepthZero() {
throw new RuntimeException(this.getClass().getSimpleName() + " does not support scopes with three or more variables");
}
public IRubyObject getValueThreeDepthZero() {
throw new RuntimeException(this.getClass().getSimpleName() + " does not support scopes with four or more variables");
}

/**
* Variation of getValue that checks for nulls, returning and setting the given value (presumably nil)
11 changes: 11 additions & 0 deletions core/src/main/java/org/jruby/runtime/scope/OneVarDynamicScope.java
Original file line number Diff line number Diff line change
@@ -68,12 +68,23 @@ public IRubyObject getValueOrNil(int offset, int depth, IRubyObject nil) {
}
}

@Override
public IRubyObject getValueDepthZero(int offset) {
return variableValueZero;
}

@Override
public IRubyObject getValueDepthZeroOrNil(int offset, IRubyObject nil) {
IRubyObject value = variableValueZero;
if (value == null) return variableValueZero = nil;
return value;
}

@Override
public IRubyObject getValueZeroDepthZero() {
return variableValueZero;
}

@Override
public IRubyObject getValueZeroDepthZeroOrNil(IRubyObject nil) {
IRubyObject value = variableValueZero;
Loading

0 comments on commit 574fd07

Please sign in to comment.