BEGIN LICENSE BLOCK ***** Version: EPL 2.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Eclipse Public License Version 2.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-v20.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. Copyright (C) 2016 The JRuby Team 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
/***** BEGIN LICENSE BLOCK ***** * Version: EPL 2.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 2.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-v20.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. * * Copyright (C) 2016 The JRuby Team * * 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.javasupport.ext; import org.jruby.*; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; import org.jruby.anno.JRubyModule; import org.jruby.internal.runtime.methods.JavaMethod; import org.jruby.javasupport.JavaClass; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.JavaInternalBlockBody; import org.jruby.runtime.Signature; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.backtrace.TraceType; import org.jruby.runtime.builtin.IRubyObject; import java.lang.reflect.Modifier; import static org.jruby.javasupport.JavaUtil.convertJavaToUsableRubyObject; import static org.jruby.javasupport.JavaUtil.isJavaObject; import static org.jruby.javasupport.JavaUtil.unwrapIfJavaObject; import static org.jruby.javasupport.JavaUtil.unwrapJavaObject; import static org.jruby.runtime.Visibility.PUBLIC;
Java::JavaLang package extensions.
Author:kares
/** * Java::JavaLang package extensions. * * @author kares */
public abstract class JavaLang { public static void define(final Ruby runtime) { JavaExtensions.put(runtime, java.lang.Iterable.class, (proxyClass) -> Iterable.define(runtime, proxyClass)); JavaExtensions.put(runtime, java.lang.Comparable.class, (proxyClass) -> Comparable.define(runtime, proxyClass)); JavaExtensions.put(runtime, java.lang.Throwable.class, (proxyClass) -> Throwable.define(runtime, (RubyClass) proxyClass)); JavaExtensions.put(runtime, java.lang.Runnable.class, (proxyClass) -> Runnable.define(runtime, proxyClass)); JavaExtensions.put(runtime, java.lang.Character.class, (proxyClass) -> Character.define(runtime, (RubyClass) proxyClass)); JavaExtensions.put(runtime, java.lang.Number.class, (proxyClass) -> Number.define(runtime, (RubyClass) proxyClass)); JavaExtensions.put(runtime, java.lang.Class.class, (proxyClass) -> Class.define(runtime, (RubyClass) proxyClass)); JavaExtensions.put(runtime, java.lang.ClassLoader.class, (proxyClass) -> ClassLoader.define(runtime, (RubyClass) proxyClass)); // Java::byte[].class_eval ... JavaExtensions.put(runtime, new byte[0].getClass(), (byteArray) -> { byteArray.addMethod("ubyte_get", new UByteGet(byteArray)); byteArray.addMethod("ubyte_set", new UByteSet(byteArray)); }); JavaExtensions.put(runtime, java.lang.String.class, (proxyClass) -> { proxyClass.defineAlias("to_str", "to_s"); }); } @JRubyModule(name = "Java::JavaLang::Iterable", include = "Enumerable") public static class Iterable { static RubyModule define(final Ruby runtime, final RubyModule proxy) { proxy.includeModule( runtime.getEnumerable() ); // include Enumerable proxy.defineAnnotatedMethods(Iterable.class); return proxy; } @JRubyMethod public static IRubyObject each(final ThreadContext context, final IRubyObject self, final Block block) { final Ruby runtime = context.runtime; if ( ! block.isGiven() ) { // ... Enumerator.new(self, :each) return runtime.getEnumerator().callMethod("new", self, runtime.newSymbol("each")); } java.lang.Iterable iterable = unwrapIfJavaObject(self); java.util.Iterator iterator = iterable.iterator(); while ( iterator.hasNext() ) { final Object value = iterator.next(); block.yield(context, convertJavaToUsableRubyObject(runtime, value)); } return self; } @JRubyMethod public static IRubyObject each_with_index(final ThreadContext context, final IRubyObject self, final Block block) { final Ruby runtime = context.runtime; if ( ! block.isGiven() ) { // ... Enumerator.new(self, :each) return runtime.getEnumerator().callMethod("new", self, runtime.newSymbol("each_with_index")); } java.lang.Iterable iterable = unwrapIfJavaObject(self); java.util.Iterator iterator = iterable.iterator(); final boolean arity2 = block.getSignature().arity() == Arity.TWO_ARGUMENTS; int i = 0; while ( iterator.hasNext() ) { final RubyInteger index = RubyFixnum.newFixnum(runtime, i++); final Object value = iterator.next(); final IRubyObject rValue = convertJavaToUsableRubyObject(runtime, value); if ( arity2 ) { block.yieldSpecific(context, rValue, index); } else { block.yield(context, RubyArray.newArray(runtime, rValue, index)); } } return self; } @JRubyMethod(name = { "to_a", "entries" }) // @override Enumerable#to_a public static IRubyObject to_a(final ThreadContext context, final IRubyObject self, final Block block) { final Ruby runtime = context.runtime; final RubyArray ary = runtime.newArray(); java.lang.Iterable iterable = unwrapIfJavaObject(self); java.util.Iterator iterator = iterable.iterator(); while ( iterator.hasNext() ) { final Object value = iterator.next(); ary.append( convertJavaToUsableRubyObject(runtime, value) ); } return ary; } @JRubyMethod(name = "count") // @override Enumerable#count public static IRubyObject count(final ThreadContext context, final IRubyObject self, final Block block) { final Ruby runtime = context.runtime; java.lang.Iterable iterable = unwrapIfJavaObject(self); if ( block.isGiven() ) { return countBlock(context, iterable.iterator(), block); } if ( iterable instanceof java.util.Collection ) { return RubyFixnum.newFixnum(runtime, ((java.util.Collection) iterable).size()); } int count = 0; for( java.util.Iterator it = iterable.iterator(); it.hasNext(); ) { it.next(); count++; } return RubyFixnum.newFixnum(runtime, count); } static RubyFixnum countBlock(final ThreadContext context, final java.util.Iterator it, final Block block) { final Ruby runtime = context.runtime; int count = 0; while ( it.hasNext() ) { IRubyObject next = convertJavaToUsableRubyObject( runtime, it.next() ); if ( block.yield( context, next ).isTrue() ) count++; } return RubyFixnum.newFixnum(runtime, count); } @JRubyMethod(name = "count") // @override Enumerable#count public static IRubyObject count(final ThreadContext context, final IRubyObject self, final IRubyObject obj, final Block unused) { // unused block due DescriptorInfo not (yet) supporting if a method receives block and an override doesn't final Ruby runtime = context.runtime; java.lang.Iterable iterable = unwrapIfJavaObject(self); int count = 0; for ( java.util.Iterator it = iterable.iterator(); it.hasNext(); ) { IRubyObject next = convertJavaToUsableRubyObject( runtime, it.next() ); if ( RubyObject.equalInternal(context, next, obj) ) count++; } return RubyFixnum.newFixnum(runtime, count); } } @JRubyClass(name = "Java::JavaLang::Comparable", include = "Comparable") public static class Comparable { static RubyModule define(final Ruby runtime, final RubyModule proxy) { proxy.includeModule( runtime.getComparable() ); // include Comparable proxy.defineAnnotatedMethods(Comparable.class); return proxy; } @JRubyMethod(name = "<=>") public static IRubyObject cmp(final ThreadContext context, final IRubyObject self, final IRubyObject other) { java.lang.Comparable comparable = unwrapIfJavaObject(self); if ( other.isNil() ) return context.nil; final java.lang.Object otherComp = unwrapIfJavaObject(other); final int cmp; try { cmp = comparable.compareTo(otherComp); } catch (ClassCastException ex) { throw context.runtime.newTypeError(ex.getMessage()); } return RubyFixnum.newFixnum(context.runtime, cmp); } } @JRubyClass(name = "Java::JavaLang::Throwable") public static class Throwable { static RubyModule define(final Ruby runtime, final RubyClass proxy) { proxy.defineAnnotatedMethods(Throwable.class); return proxy; } @JRubyMethod // stackTrace => backtrace public static IRubyObject backtrace(final ThreadContext context, final IRubyObject self) { final Ruby runtime = context.runtime; java.lang.Throwable throwable = unwrapIfJavaObject(self); // TODO instead this should get aligned with NativeException !?! StackTraceElement[] stackTrace = throwable.getStackTrace(); if ( stackTrace == null ) return context.nil; // never actually happens final int len = stackTrace.length; if ( len == 0 ) return RubyArray.newEmptyArray(runtime); IRubyObject[] backtrace = new IRubyObject[len]; for ( int i=0; i < len; i++ ) { backtrace[i] = RubyString.newString(runtime, stackTrace[i].toString()); } return RubyArray.newArrayMayCopy(runtime, backtrace); } @JRubyMethod // can not set backtrace for a java.lang.Throwable public static IRubyObject set_backtrace(final IRubyObject self, final IRubyObject backtrace) { return self.getRuntime().getNil(); } @JRubyMethod public static IRubyObject message(final ThreadContext context, final IRubyObject self) { java.lang.Throwable throwable = unwrapIfJavaObject(self); final String msg = throwable.getLocalizedMessage(); // does getMessage return msg == null ? RubyString.newEmptyString(context.runtime) : RubyString.newString(context.runtime, msg); } @JRubyMethod public static IRubyObject full_message(final ThreadContext context, final IRubyObject self) { return full_message(context, self, null); } @JRubyMethod public static IRubyObject full_message(final ThreadContext context, final IRubyObject self, final IRubyObject opts) { return RubyString.newString(context.runtime, TraceType.printFullMessage(context, self, opts)); } @JRubyMethod // Ruby exception to_s is the same as message public static IRubyObject to_s(final ThreadContext context, final IRubyObject self) { return message(context, self); } @JRubyMethod public static IRubyObject inspect(final ThreadContext context, final IRubyObject self) { java.lang.Throwable throwable = unwrapIfJavaObject(self); return RubyString.newString(context.runtime, throwable.toString()); } @JRubyMethod(name = "===", meta = true) public static IRubyObject eqq(final ThreadContext context, final IRubyObject self, IRubyObject other) { if (checkNativeException(self, other)) { return context.tru; } return self.op_eqq(context, other); } @SuppressWarnings("deprecation") private static boolean checkNativeException(IRubyObject self, IRubyObject other) { if ( other instanceof NativeException ) { final java.lang.Class java_class = (java.lang.Class) self.dataGetStruct(); if ( java_class.isAssignableFrom( ((NativeException) other).getCause().getClass() ) ) { return true; } } return false; } } @JRubyModule(name = "Java::JavaLang::Runnable") public static class Runnable { static RubyModule define(final Ruby runtime, final RubyModule proxy) { proxy.defineAnnotatedMethods(Runnable.class); return proxy; } @JRubyMethod public static IRubyObject to_proc(final ThreadContext context, final IRubyObject self) { final Ruby runtime = context.runtime; final java.lang.Runnable runnable = unwrapIfJavaObject(self); final Block block = new Block(new RunBody(runtime, runnable)); return new RubyProc(runtime, runtime.getProc(), block, null, -1); } private static final class RunBody extends JavaInternalBlockBody { private final java.lang.Runnable runnable; RunBody(final Ruby runtime, final java.lang.Runnable runnable) { super(runtime, Signature.NO_ARGUMENTS); this.runnable = runnable; } @Override public IRubyObject yield(ThreadContext context, IRubyObject[] args) { return yieldImpl(context); } final IRubyObject yieldImpl(ThreadContext context) { runnable.run(); return context.nil; } @Override protected final IRubyObject doYield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) { return yieldImpl(context); } @Override protected final IRubyObject doYield(ThreadContext context, Block block, IRubyObject value) { return yieldImpl(context); // avoid new IRubyObject[] { value } } } } @JRubyClass(name = "Java::JavaLang::Number") public static class Number { static RubyClass define(final Ruby runtime, final RubyClass proxy) { proxy.defineAnnotatedMethods(Number.class); proxy.defineAlias("to_int", "longValue"); proxy.defineAlias("to_f", "doubleValue"); return proxy; } @JRubyMethod(name = "to_f") public static IRubyObject to_f(final ThreadContext context, final IRubyObject self) { java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class); return context.runtime.newFloat(val.doubleValue()); } @JRubyMethod(name = "real?") public static IRubyObject real_p(final ThreadContext context, final IRubyObject self) { java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class); return context.runtime.newBoolean(val instanceof Integer || val instanceof Long || val instanceof Short || val instanceof Byte || val instanceof Float || val instanceof Double || val instanceof java.math.BigInteger || val instanceof java.math.BigDecimal); } @JRubyMethod(name = { "to_i", "to_int" }) public static IRubyObject to_i(final ThreadContext context, final IRubyObject self) { java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class); if (val instanceof java.math.BigInteger) { // NOTE: should be moved into its own? return RubyBignum.newBignum(context.runtime, (java.math.BigInteger) val); } return context.runtime.newFixnum(val.longValue()); } @JRubyMethod(name = "integer?") public static IRubyObject integer_p(final ThreadContext context, final IRubyObject self) { java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class); return context.runtime.newBoolean(val instanceof Integer || val instanceof Long || val instanceof Short || val instanceof Byte || val instanceof java.math.BigInteger); } @JRubyMethod(name = "zero?") public static IRubyObject zero_p(final ThreadContext context, final IRubyObject self) { return context.runtime.newBoolean(isZero(self)); } private static boolean isZero(final IRubyObject self) { java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class); return Double.compare(val.doubleValue(), 0) == 0; } @JRubyMethod(name = "nonzero?") public static IRubyObject nonzero_p(final ThreadContext context, final IRubyObject self) { return isZero(self) ? context.nil : self; } @JRubyMethod(name = "coerce") public static IRubyObject coerce(final ThreadContext context, final IRubyObject self, final IRubyObject type) { java.lang.Number val = (java.lang.Number) self.toJava(java.lang.Number.class); // NOTE: a basic stub that always coverts Java numbers to Ruby ones (for simplicity) // gist being this is not expected to be used heavily, if so should get special care final IRubyObject value = convertJavaToUsableRubyObject(context.runtime, val); return context.runtime.newArray(type, value); } } @JRubyClass(name = "Java::JavaLang::Character") public static class Character { static RubyClass define(final Ruby runtime, final RubyClass proxy) { proxy.defineAnnotatedMethods(Character.class); return proxy; } @JRubyMethod(name = "java_identifier_start?", meta = true) public static IRubyObject java_identifier_start_p(final ThreadContext context, final IRubyObject self, final IRubyObject num) { return context.runtime.newBoolean( java.lang.Character.isJavaIdentifierStart(int_char(num)) ); } @JRubyMethod(name = "java_identifier_part?", meta = true) public static IRubyObject java_identifier_part_p(final ThreadContext context, final IRubyObject self, final IRubyObject num) { return context.runtime.newBoolean( java.lang.Character.isJavaIdentifierPart(int_char(num)) ); } private static int int_char(IRubyObject num) { // str.ord -> Fixnum return num.toJava(java.lang.Character.TYPE); } @JRubyMethod(name = "to_i") public static IRubyObject to_i(final ThreadContext context, final IRubyObject self) { java.lang.Character c = (java.lang.Character) self.toJava(java.lang.Character.class); return context.runtime.newFixnum(c); } } @JRubyClass(name = "Java::JavaLang::Class") public static class Class { static RubyClass define(final Ruby runtime, final RubyClass proxy) { proxy.includeModule( runtime.getComparable() ); // include Comparable proxy.defineAnnotatedMethods(Class.class); return proxy; } @JRubyMethod(name = "ruby_class") public static IRubyObject proxy_class(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return context.runtime.getJavaSupport().getProxyClassFromCache(klass); } @JRubyMethod public static IRubyObject resource_as_stream(final ThreadContext context, final IRubyObject self, final IRubyObject name) { final java.lang.Class klass = unwrapJavaObject(self); final String resName = name.convertToString().toString(); return convertJavaToUsableRubyObject(context.runtime, klass.getResourceAsStream(resName)); } @JRubyMethod public static IRubyObject resource_as_string(final ThreadContext context, final IRubyObject self, final IRubyObject name) { final java.lang.Class klass = unwrapJavaObject(self); final String resName = name.convertToString().toString(); return new RubyIO(context.runtime, klass.getResourceAsStream(resName)).read(context); } @JRubyMethod // alias to_s name public static IRubyObject to_s(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return RubyString.newString(context.runtime, klass.getName()); } @JRubyMethod public static IRubyObject inspect(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return RubyString.newString(context.runtime, klass.toString()); } @JRubyMethod(name = "annotations?") public static IRubyObject annotations_p(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return context.runtime.newBoolean(klass.getAnnotations().length > 0); } @JRubyMethod(name = "declared_annotations?") public static IRubyObject declared_annotations_p(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return context.runtime.newBoolean(klass.getDeclaredAnnotations().length > 0); } @JRubyMethod public static IRubyObject java_instance_methods(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); final RubyArray methods = RubyArray.newArray(context.runtime); for ( java.lang.reflect.Method method : klass.getMethods() ) { if ( ! Modifier.isStatic(method.getModifiers()) ) methods.add(method); } return methods; } @JRubyMethod public static IRubyObject declared_instance_methods(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); final RubyArray methods = RubyArray.newArray(context.runtime); for ( java.lang.reflect.Method method : klass.getDeclaredMethods() ) { if ( ! Modifier.isStatic(method.getModifiers()) ) methods.add(method); } return methods; } @JRubyMethod public static IRubyObject java_class_methods(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); final RubyArray methods = RubyArray.newArray(context.runtime); for ( java.lang.reflect.Method method : klass.getMethods() ) { if ( Modifier.isStatic(method.getModifiers()) ) methods.add(method); } return methods; } @JRubyMethod public static IRubyObject declared_class_methods(final ThreadContext context, final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); final RubyArray methods = RubyArray.newArray(context.runtime); for ( java.lang.reflect.Method method : klass.getDeclaredMethods() ) { if ( Modifier.isStatic(method.getModifiers()) ) methods.add(method); } return methods; } @JRubyMethod(name = "<=>") // Ruby Comparable public static IRubyObject cmp(final ThreadContext context, final IRubyObject self, final IRubyObject other) { final java.lang.Class that; if ( other instanceof JavaClass ) { that = ((JavaClass) other).getJavaClass(); } else if ( isJavaObject(other) ) { that = unwrapJavaObject(other); } else { return context.nil; } final java.lang.Class thiz = unwrapJavaObject(self); if ( thiz == that ) return context.runtime.newFixnum(0); if ( thiz.isAssignableFrom(that) ) return context.runtime.newFixnum(+1); if ( that.isAssignableFrom(thiz) ) return context.runtime.newFixnum(-1); return context.nil; } @JRubyMethod(name = "anonymous?") public static IRubyObject anonymous_p(final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return self.getRuntime().newBoolean( klass.isAnonymousClass() ); } @JRubyMethod(name = "abstract?") public static IRubyObject abstract_p(final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return JavaLangReflect.isAbstract( self, klass.getModifiers() ); } // JavaUtilities::ModifiedShortcuts : @JRubyMethod(name = "public?") public static IRubyObject public_p(final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return JavaLangReflect.isPublic( self, klass.getModifiers() ); } @JRubyMethod(name = "protected?") public static IRubyObject protected_p(final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return JavaLangReflect.isProtected(self, klass.getModifiers()); } @JRubyMethod(name = "private?") public static IRubyObject private_p(final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return JavaLangReflect.isPrivate(self, klass.getModifiers()); } @JRubyMethod(name = "final?") public static IRubyObject final_p(final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return JavaLangReflect.isFinal(self, klass.getModifiers()); } @JRubyMethod(name = "static?") public static IRubyObject static_p(final IRubyObject self) { final java.lang.Class klass = unwrapJavaObject(self); return JavaLangReflect.isStatic(self, klass.getModifiers()); } } @JRubyClass(name = "Java::JavaLang::ClassLoader") public static class ClassLoader { static RubyModule define(final Ruby runtime, final RubyClass proxy) { proxy.defineAnnotatedMethods(ClassLoader.class); return proxy; } @JRubyMethod public static IRubyObject resource_as_url(final ThreadContext context, final IRubyObject self, final IRubyObject name) { final java.lang.ClassLoader loader = unwrapIfJavaObject(self); final String resName = name.convertToString().toString(); return convertJavaToUsableRubyObject(context.runtime, loader.getResource(resName)); } @JRubyMethod public static IRubyObject resource_as_stream(final ThreadContext context, final IRubyObject self, final IRubyObject name) { final java.lang.ClassLoader loader = unwrapIfJavaObject(self); final String resName = name.convertToString().toString(); return convertJavaToUsableRubyObject(context.runtime, loader.getResourceAsStream(resName)); } @JRubyMethod public static IRubyObject resource_as_string(final ThreadContext context, final IRubyObject self, final IRubyObject name) { final java.lang.ClassLoader loader = unwrapIfJavaObject(self); final String resName = name.convertToString().toString(); return new RubyIO(context.runtime, loader.getResourceAsStream(resName)).read(context); } } private static final class UByteGet extends JavaMethod.JavaMethodOne { UByteGet(RubyModule implClass) { super(implClass, PUBLIC, "ubyte_get"); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject idx) { final RubyInteger val = (RubyInteger) self.callMethod(context, "[]", idx); int byte_val = val.getIntValue(); if ( byte_val >= 0 ) return val; return RubyFixnum.newFixnum(context.runtime, byte_val + 256); // byte += 256 if byte < 0 } } private static final class UByteSet extends JavaMethod.JavaMethodTwo { UByteSet(RubyModule implClass) { super(implClass, PUBLIC, "ubyte_set"); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject idx, IRubyObject val) { int byte_val = ((RubyInteger) val).getIntValue(); if ( byte_val > 127 ) { val = RubyFixnum.newFixnum(context.runtime, byte_val - 256); // value -= 256 if value > 127 } return self.callMethod(context, "[]=", new IRubyObject[] { idx, val }); } } }