org.jruby.RubyBasicObject Maven / Gradle / Ivy
/***** 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.
*
* Copyright (C) 2008 Thomas E Enebo
*
* 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 org.jcodings.Encoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.JavaSites.BasicObjectSites;
import org.jruby.runtime.ivars.VariableAccessor;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jruby.anno.JRubyMethod;
import org.jruby.common.IRubyWarnings.ID;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.javasupport.JavaObject;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import static org.jruby.anno.FrameField.*;
import static org.jruby.runtime.Helpers.invokeChecked;
import static org.jruby.runtime.Visibility.*;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Arity;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.builtin.InstanceVariables;
import org.jruby.runtime.builtin.InternalVariables;
import org.jruby.runtime.builtin.Variable;
import org.jruby.runtime.component.VariableEntry;
import org.jruby.runtime.marshal.CoreObjectType;
import org.jruby.util.ArraySupport;
import org.jruby.util.ConvertBytes;
import org.jruby.util.IdUtil;
import org.jruby.util.TypeConverter;
import org.jruby.util.unsafe.UnsafeHolder;
import static org.jruby.runtime.Helpers.invokedynamic;
import static org.jruby.runtime.invokedynamic.MethodNames.OP_EQUAL;
import static org.jruby.runtime.invokedynamic.MethodNames.OP_CMP;
import static org.jruby.runtime.invokedynamic.MethodNames.EQL;
import static org.jruby.runtime.invokedynamic.MethodNames.INSPECT;
import static org.jruby.util.io.EncodingUtils.encStrBufCat;
import static org.jruby.util.io.EncodingUtils.strBufCat;
import org.jruby.runtime.ivars.VariableTableManager;
/**
* RubyBasicObject is the only implementation of the
* {@link org.jruby.runtime.builtin.IRubyObject}. Every Ruby object in JRuby
* is represented by something that is an instance of RubyBasicObject. In
* the core class implementations, this means doing a subclass
* that extends RubyBasicObject. In other cases it means using a simple
* RubyBasicObject instance and its data fields to store specific
* information about the Ruby object.
*
* Some care has been taken to make the implementation be as
* monomorphic as possible, so that the Java Hotspot engine can
* improve performance of it. That is the reason for several patterns
* that might seem odd in this class.
*
* The IRubyObject interface used to have lots of methods for
* different things, but these have now mostly been refactored into
* several interfaces that gives access to that specific part of the
* object. This gives us the possibility to switch out that subsystem
* without changing interfaces again. For example, instance variable
* and internal variables are handled this way, but the implementation
* in RubyObject only returns "this" in {@link #getInstanceVariables()} and
* {@link #getInternalVariables()}.
*
* Methods that are implemented here, such as "initialize" should be implemented
* with care; reification of Ruby classes into Java classes can produce
* conflicting method names in rare cases. See JRUBY-5906 for an example.
*/
public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Comparable, CoreObjectType, InstanceVariables, InternalVariables {
//private static final Logger LOG = LoggerFactory.getLogger(RubyBasicObject.class);
//private static final boolean DEBUG = false;
/** The class of this object */
protected transient RubyClass metaClass;
/** object flags */
protected int flags;
/** variable table, lazily allocated as needed (if needed) */
public transient Object[] varTable;
/** locking stamp for Unsafe ops updating the vartable */
public transient volatile int varTableStamp;
/** offset of the varTable field in RubyBasicObject */
public static final long VAR_TABLE_OFFSET = UnsafeHolder.fieldOffset(RubyBasicObject.class, "varTable");
/** offset of the varTableTamp field in RubyBasicObject */
public static final long STAMP_OFFSET = UnsafeHolder.fieldOffset(RubyBasicObject.class, "varTableStamp");
/**
* The error message used when some one tries to modify an
* instance variable in a high security setting.
*/
public static final String ERR_INSECURE_SET_INST_VAR = "Insecure: can't modify instance variable";
public static final int ALL_F = -1;
public static final int FALSE_F = ObjectFlags.FALSE_F;
/**
* This flag is a bit funny. It's used to denote that this value
* is nil. It's a bit counterintuitive for a Java programmer to
* not use subclassing to handle this case, since we have a
* RubyNil subclass anyway. Well, the reason for it being a flag
* is that the {@link #isNil()} method is called extremely often. So often
* that it gives a good speed boost to make it monomorphic and
* final. It turns out using a flag for this actually gives us
* better performance than having a polymorphic {@link #isNil()} method.
*/
public static final int NIL_F = ObjectFlags.NIL_F;
public static final int FROZEN_F = ObjectFlags.FROZEN_F;
public static final int TAINTED_F = ObjectFlags.TAINTED_F;
/**
* A value that is used as a null sentinel in among other places
* the RubyArray implementation. It will cause large problems to
* call any methods on this object.
*/
public static final IRubyObject NEVER = new RubyBasicObject();
/**
* A value that specifies an undefined value. This value is used
* as a sentinel for undefined constant values, and other places
* where neither null nor NEVER makes sense.
*/
public static final IRubyObject UNDEF = new RubyBasicObject();
/**
* It's not valid to create a totally empty RubyObject. Since the
* RubyObject is always defined in relation to a runtime, that
* means that creating RubyObjects from outside the class might
* cause problems.
*/
private RubyBasicObject(){};
/**
* Default allocator instance for all Ruby objects. The only
* reason to not use this allocator is if you actually need to
* have all instances of something be a subclass of RubyObject.
*
* @see org.jruby.runtime.ObjectAllocator
*/
public static final ObjectAllocator BASICOBJECT_ALLOCATOR = new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyBasicObject(runtime, klass);
}
};
/**
* Will create the Ruby class Object in the runtime
* specified. This method needs to take the actual class as an
* argument because of the Object class' central part in runtime
* initialization.
*/
public static RubyClass createBasicObjectClass(Ruby runtime, RubyClass objectClass) {
objectClass.setClassIndex(ClassIndex.OBJECT);
objectClass.defineAnnotatedMethods(RubyBasicObject.class);
recacheBuiltinMethods(runtime);
return objectClass;
}
static void recacheBuiltinMethods(Ruby runtime) {
RubyModule objectClass = runtime.getBasicObject();
// Since method_missing is marked module we actually define two builtin versions
runtime.setDefaultMethodMissing(objectClass.searchMethod("method_missing"),
objectClass.getMetaClass().searchMethod("method_missing"));
}
@JRubyMethod(name = "initialize", visibility = PRIVATE)
public IRubyObject initialize(ThreadContext context) {
return context.nil;
}
@Deprecated
public IRubyObject initialize19(ThreadContext context) {
return initialize(context);
}
//public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
// return initialize(context);
//}
/**
* Standard path for object creation. Objects are entered into ObjectSpace
* only if ObjectSpace is enabled.
*/
public RubyBasicObject(Ruby runtime, RubyClass metaClass) {
this.metaClass = metaClass;
runtime.addToObjectSpace(true, this);
}
/**
* Path for objects that don't taint and don't enter objectspace.
*/
public RubyBasicObject(RubyClass metaClass) {
this.metaClass = metaClass;
}
/**
* Path for objects who want to decide whether they don't want to be in
* ObjectSpace even when it is on. (notably used by objects being
* considered immediate, they'll always pass false here)
*/
protected RubyBasicObject(Ruby runtime, RubyClass metaClass, boolean useObjectSpace) {
this.metaClass = metaClass;
runtime.addToObjectSpace(useObjectSpace, this);
}
protected void taint(Ruby runtime) {
if (!isTaint()) {
testFrozen();
setTaint(true);
}
}
/** rb_frozen_class_p
*
* Helper to test whether this object is frozen, and if it is will
* throw an exception based on the message.
*/
protected final void testFrozen(String message) {
if (isFrozen()) {
throw getRuntime().newFrozenError(message);
}
}
/** rb_frozen_class_p
*
* Helper to test whether this object is frozen, and if it is will
* throw an exception based on the message.
*/
protected final void testFrozen() {
if (isFrozen()) {
throw getRuntime().newFrozenError("object");
}
}
/**
* Sets or unsets a flag on this object. The only flags that are
* guaranteed to be valid to use as the first argument is:
*
*
* - {@link #FALSE_F}
* - {@link #NIL_F}
* - {@link #FROZEN_F}
* - {@link #TAINTED_F}
*
*
* @param flag the actual flag to set or unset.
* @param set if true, the flag will be set, if false, the flag will be unset.
*/
public final void setFlag(int flag, boolean set) {
if (set) {
flags |= flag;
} else {
flags &= ~flag;
}
}
/**
* Get the value of a custom flag on this object. The only
* guaranteed flags that can be sent in to this method is:
*
*
* - {@link #FALSE_F}
* - {@link #NIL_F}
* - {@link #FROZEN_F}
* - {@link #TAINTED_F}
*
*
* @param flag the flag to get
* @return true if the flag is set, false otherwise
*/
public final boolean getFlag(int flag) {
return (flags & flag) != 0;
}
/**
* Will invoke a named method with no arguments and no block if that method or a custom
* method missing exists. Otherwise returns null. 1.9: rb_check_funcall
*/
@Override
public final IRubyObject checkCallMethod(ThreadContext context, String name) {
return Helpers.invokeChecked(context, this, name);
}
/**
* Will invoke a named method with no arguments and no block if that method or a custom
* method missing exists. Otherwise returns null. 1.9: rb_check_funcall
*/
@Override
public final IRubyObject checkCallMethod(ThreadContext context, JavaSites.CheckedSites sites) {
return Helpers.invokeChecked(context, this, sites);
}
/**
* Will invoke a named method with no arguments and no block.
*/
@Override
public final IRubyObject callMethod(ThreadContext context, String name) {
return Helpers.invoke(context, this, name);
}
/**
* Will invoke a named method with one argument and no block with
* functional invocation.
*/
@Override
public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject arg) {
return Helpers.invoke(context, this, name, arg);
}
/**
* Will invoke a named method with the supplied arguments and no
* block with functional invocation.
*/
@Override
public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject[] args) {
return Helpers.invoke(context, this, name, args);
}
public final IRubyObject callMethod(String name, IRubyObject... args) {
return Helpers.invoke(getRuntime().getCurrentContext(), this, name, args);
}
public final IRubyObject callMethod(String name) {
return Helpers.invoke(getRuntime().getCurrentContext(), this, name);
}
/**
* Will invoke a named method with the supplied arguments and
* supplied block with functional invocation.
*/
@Override
public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject[] args, Block block) {
return Helpers.invoke(context, this, name, args, block);
}
/**
* Does this object represent nil? See the docs for the {@link
* #NIL_F} flag for more information.
*/
@Override
public final boolean isNil() {
return (flags & NIL_F) != 0;
}
/**
* Is this value a truthy value or not? Based on the {@link #FALSE_F} flag.
*/
@Override
public final boolean isTrue() {
return (flags & FALSE_F) == 0;
}
/**
* Is this value a falsey value or not? Based on the {@link #FALSE_F} flag.
*/
public final boolean isFalse() {
return (flags & FALSE_F) != 0;
}
/**
* Gets the taint. Shortcut for getFlag(TAINTED_F).
*
* @return true if this object is tainted
*/
@Override
public boolean isTaint() {
return (flags & TAINTED_F) != 0;
}
/**
* Sets the taint flag. Shortcut for setFlag(TAINTED_F, taint)
*
* @param taint should this object be tainted or not?
*/
@Override
public void setTaint(boolean taint) {
// JRUBY-4113: callers should not call setTaint on immediate objects
if (isImmediate()) return;
if (taint) {
flags |= TAINTED_F;
} else {
flags &= ~TAINTED_F;
}
}
/** OBJ_INFECT
*
* Infects this object with traits from the argument obj. In real
* terms this currently means that if obj is tainted, this object
* will get tainted too. It's possible to hijack this method to do
* other infections if that would be interesting.
*/
@Override
public IRubyObject infectBy(IRubyObject obj) {
if (obj.isTaint()) setTaint(true);
return this;
}
final RubyBasicObject infectBy(RubyBasicObject obj) {
flags |= (obj.flags & TAINTED_F);
return this;
}
final RubyBasicObject infectBy(int tuFlags) {
flags |= (tuFlags & TAINTED_F);
return this;
}
/**
* Is this value frozen or not? Shortcut for doing
* getFlag(FROZEN_F).
*
* @return true if this object is frozen, false otherwise
*/
@Override
public boolean isFrozen() {
return (flags & FROZEN_F) != 0;
}
/**
* Sets whether this object is frozen or not. Shortcut for doing
* setFlag(FROZEN_F, frozen).
*
* @param frozen should this object be frozen?
*/
@Override
public void setFrozen(boolean frozen) {
if (frozen) {
flags |= FROZEN_F;
} else {
flags &= ~FROZEN_F;
}
}
/**
* Is object immediate (def: Fixnum, Symbol, true, false, nil?).
*/
@Override
public boolean isImmediate() {
return false;
}
@Override
public boolean isSpecialConst() {
return isImmediate() || !isTrue();
}
/**
* if exist return the meta-class else return the type of the object.
*
*/
@Override
public final RubyClass getMetaClass() {
return metaClass;
}
/** rb_singleton_class
*
* Note: this method is specialized for RubyFixnum, RubySymbol,
* RubyNil and RubyBoolean
*
* Will either return the existing singleton class for this
* object, or create a new one and return that.
*/
@Override
public RubyClass getSingletonClass() {
RubyClass klass;
if (getMetaClass().isSingleton() && ((MetaClass)getMetaClass()).getAttached() == this) {
klass = getMetaClass();
} else {
klass = makeMetaClass(getMetaClass());
}
klass.setTaint(isTaint());
if (isFrozen()) klass.setFrozen(true);
return klass;
}
/** rb_make_metaclass
*
* Will create a new meta class, insert this in the chain of
* classes for this specific object, and return the generated meta
* class.
*/
public RubyClass makeMetaClass(RubyClass superClass) {
MetaClass klass = new MetaClass(getRuntime(), superClass, this); // rb_class_boot
setMetaClass(klass);
klass.setMetaClass(superClass.getRealClass().getMetaClass());
superClass.addSubclass(klass);
return klass;
}
/**
* Makes it possible to change the metaclass of an object. In
* practice, this is a simple version of Smalltalks Become, except
* that it doesn't work when we're dealing with subclasses. In
* practice it's used to change the singleton/meta class used,
* without changing the "real" inheritance chain.
*/
public void setMetaClass(RubyClass metaClass) {
this.metaClass = metaClass;
}
/**
* @see org.jruby.runtime.builtin.IRubyObject#getType()
*/
@Override
public final RubyClass getType() {
return metaClass.getRealClass();
}
/**
* Does this object respond to the specified message? Uses a
* shortcut if it can be proved that respond_to? and respond_to_missing?
* haven't been overridden.
*/
@Override
public final boolean respondsTo(String name) {
final Ruby runtime = getRuntime();
final DynamicMethod respondTo = getMetaClass().searchMethod("respond_to?");
// fastest path; builtin respond_to? and respond_to_missing? so we just check isMethodBound
if ( respondTo.equals(runtime.getRespondToMethod()) &&
getMetaClass().searchMethod("respond_to_missing?").equals(runtime.getRespondToMissingMethod()) ) {
return getMetaClass().respondsToMethod(name, false);
}
final ThreadContext context = runtime.getCurrentContext();
final RubySymbol mname = runtime.newSymbol(name);
final boolean respondToUndefined = respondTo.isUndefined();
// respond_to? or respond_to_missing? is not defined, so we must dispatch to trigger method_missing
if ( respondToUndefined ) {
return sites(context).respond_to.call(context, this, this, mname).isTrue();
}
// respond_to? is defined, invoke already-retrieved method object
final String respondName = "respond_to?";
// We have to check and enforce arity
final Arity arity = respondTo.getArity();
if ( arity.isFixed() ) {
if ( arity.required() == 1 ) {
return respondTo.call(context, this, metaClass, respondName, mname).isTrue();
}
if ( arity.required() != 2 ) {
throw runtime.newArgumentError(respondName + " must accept 1 or 2 arguments (requires " + arity.getValue() + ")");
}
}
return respondTo.call(context, this, metaClass, respondName, mname, runtime.getTrue()).isTrue();
}
/**
* Does this object respond to the specified message via "method_missing?"
*/
@Override
public final boolean respondsToMissing(String name) {
return respondsToMissing(name, true);
}
/**
* Does this object respond to the specified message via "method_missing?"
*/
@Override
public final boolean respondsToMissing(String name, boolean incPrivate) {
DynamicMethod method = getMetaClass().searchMethod("respond_to_missing?");
// perhaps should try a smart version as for respondsTo above?
if ( method.isUndefined() ) return false;
final Ruby runtime = getRuntime();
return method.call(runtime.getCurrentContext(), this, getMetaClass(),
"respond_to_missing?", runtime.newSymbol(name), runtime.newBoolean(incPrivate)
).isTrue();
}
/**
* Will return the runtime that this object is associated with.
*
* @return current runtime
*/
@Override
public final Ruby getRuntime() {
return getMetaClass().getClassRuntime();
}
/**
* Will return the Java interface that most closely can represent
* this object, when working through JAva integration
* translations.
*/
@Override
public Class getJavaClass() {
Object obj = dataGetStruct();
if (obj instanceof JavaObject) {
return ((JavaObject)obj).getValue().getClass();
}
return getClass();
}
/** rb_to_id
*
* Will try to convert this object to a String using the Ruby
* "to_str" if the object isn't already a String. If this still
* doesn't work, will throw a Ruby TypeError.
*
*/
@Override
public String asJavaString() {
IRubyObject asString = checkStringType();
if(!asString.isNil()) return ((RubyString)asString).asJavaString();
throw getRuntime().newTypeError(inspect().toString() + " is not a string");
}
/** rb_obj_as_string
*
* First converts this object into a String using the "to_s"
* method, infects it with the current taint and returns it. If
* to_s doesn't return a Ruby String, {@link #anyToString} is used
* instead.
*/
@Override
public RubyString asString() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
IRubyObject str = sites.to_s.call(context, this, this);
if (!(str instanceof RubyString)) return (RubyString)anyToString();
if (isTaint()) str.setTaint(true);
return (RubyString) str;
}
/**
* Tries to convert this object to a Ruby Array using the "to_ary"
* method.
*/
@Override
public RubyArray convertToArray() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
return (RubyArray) TypeConverter.convertToType(context, this, runtime.getArray(), sites.to_ary_checked);
}
/**
* Tries to convert this object to a Ruby Hash using the "to_hash"
* method.
*/
@Override
public RubyHash convertToHash() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
return (RubyHash) TypeConverter.convertToType(context, this, runtime.getHash(), sites.to_hash_checked);
}
/**
* Tries to convert this object to a Ruby Float using the "to_f"
* method.
*/
@Override
public RubyFloat convertToFloat() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
return (RubyFloat) TypeConverter.convertToType(context, this, runtime.getFloat(), sites.to_f_checked);
}
/**
* Tries to convert this object to a Ruby Integer using the "to_int"
* method.
*/
@Override
public RubyInteger convertToInteger() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
IRubyObject result = TypeConverter.convertToType(context, this, runtime.getInteger(), sites.to_int_checked, true);
if (!(result instanceof RubyInteger)) throw getRuntime().newTypeError(getMetaClass().getName() + "#to_int should return Integer");
return (RubyInteger) result;
}
/**
* Tries to convert this object to a Ruby Integer using the
* supplied conversion method.
*/
@Override
public RubyInteger convertToInteger(String convertMethod) {
if (convertMethod.equals("to_int")) return convertToInteger();
IRubyObject result;
if (convertMethod.equals("to_i")) {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
result = TypeConverter.convertToType(context, this, runtime.getInteger(), sites.to_i_checked, true);
} else {
result = TypeConverter.convertToType(this, getRuntime().getInteger(), convertMethod, true);
}
if (!(result instanceof RubyInteger)) throw getRuntime().newTypeError(getMetaClass().getName() + "#to_int should return Integer");
return (RubyInteger) result;
}
/**
* Tries to convert this object to a Ruby String using the
* "to_str" method.
*/
@Override
public RubyString convertToString() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
return (RubyString) TypeConverter.convertToType(context, this, getRuntime().getString(), sites.to_str_checked);
}
/**
* Internal method that helps to convert any object into the
* format of a class name and a hex string inside of #<>.
*/
@Override
public IRubyObject anyToString() {
String cname = getMetaClass().getRealClass().getName();
String hex = Integer.toHexString(System.identityHashCode(this));
/* 6:tags 16:addr 1:eos */
RubyString str = RubyString.newString(getRuntime(),
new StringBuilder(2 + cname.length() + 3 + hex.length() + 1).
append("#<").append(cname).append(":0x").append(hex).append('>')
);
str.setTaint(isTaint());
return str;
}
/** rb_check_string_type
*
* Tries to return a coerced string representation of this object,
* using "to_str". If that returns something other than a String
* or nil, an empty String will be returned.
*
*/
@Override
public IRubyObject checkStringType() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
return TypeConverter.checkStringType(context, sites.to_str_checked, this);
}
/** rb_check_string_type
*
* Tries to return a coerced string representation of this object,
* using "to_str". If that returns something other than a String
* or nil, an empty String will be returned.
*
*/
@Override
public IRubyObject checkStringType19() {
return checkStringType();
}
/** rb_check_array_type
*
* Returns the result of trying to convert this object to an Array
* with "to_ary".
*/
@Override
public IRubyObject checkArrayType() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
BasicObjectSites sites = sites(context);
return TypeConverter.checkArrayType(context, sites.to_ary_checked, this);
}
/**
* @see IRubyObject#toJava
*/
@Override
public Object toJava(Class target) {
return defaultToJava(target);
}
final T defaultToJava(Class target) {
// for callers that unconditionally pass null retval type (JRUBY-4737)
if (target == void.class) return null;
final Object innerWrapper = dataGetStruct();
if (innerWrapper instanceof JavaObject) {
// for interface impls
final Object value = ((JavaObject) innerWrapper).getValue();
// ensure the object is associated with the wrapper we found it in,
// so that if it comes back we don't re-wrap it
if (target.isAssignableFrom(value.getClass())) {
getRuntime().getJavaSupport().getObjectProxyCache().put(value, this);
return (T) value;
}
}
else if (JavaUtil.isDuckTypeConvertable(getClass(), target)) {
if (!respondsTo("java_object")) {
return JavaUtil.convertProcToInterface(getRuntime().getCurrentContext(), this, target);
}
}
else if (target.isAssignableFrom(getClass())) {
return (T) this;
}
throw getRuntime().newTypeError("cannot convert instance of " + getClass() + " to " + target);
}
@Override
public IRubyObject dup() {
Ruby runtime = getRuntime();
if (isImmediate()) throw runtime.newTypeError("can't dup " + getMetaClass().getName());
IRubyObject dup = getMetaClass().getRealClass().allocate();
if (isTaint()) dup.setTaint(true);
initCopy(runtime.getCurrentContext(), dup, this, false);
return dup;
}
/** init_copy
*
* Initializes a copy with variable and special instance variable
* information, and then call the initialize_copy Ruby method.
*/
private static IRubyObject initCopy(ThreadContext context, IRubyObject clone, IRubyObject original, boolean doClone) {
assert !clone.isFrozen() : "frozen object (" + clone.getMetaClass().getName() + ") allocated";
original.copySpecialInstanceVariables(clone);
if (original.hasVariables()) clone.syncVariables(original);
if (original instanceof RubyModule) {
RubyModule cloneMod = (RubyModule)clone;
cloneMod.syncConstants((RubyModule)original);
cloneMod.syncClassVariables((RubyModule)original);
}
/* FIXME: finalizer should be dupped here */
return doClone ?
sites(context).initialize_clone.call(context, clone, clone, original) :
sites(context).initialize_dup.call(context, clone, clone, original);
}
protected static boolean OBJ_INIT_COPY(IRubyObject obj, IRubyObject orig) {
if (obj == orig) return false;
objInitCopy(obj, orig);
return true;
}
protected static void objInitCopy(IRubyObject obj, IRubyObject orig) {
if (obj == orig) return;
// FIXME: booooo!
((RubyBasicObject)obj).checkFrozen();
// Not implemented
// checkTrusted();
if (obj.getClass() != orig.getClass() || obj.getMetaClass().getRealClass() != orig.getMetaClass().getRealClass()) {
throw obj.getRuntime().newTypeError("initialize_copy should take same class object");
}
}
/**
* Lots of MRI objects keep their state in non-lookupable ivars
* (e:g. Range, Struct, etc). This method is responsible for
* dupping our java field equivalents
*/
@Override
public void copySpecialInstanceVariables(IRubyObject clone) {
}
/** rb_inspect
*
* The internal helper that ensures a RubyString instance is returned
* so dangerous casting can be omitted
* Preferred over callMethod(context, "inspect")
*/
static RubyString inspect(ThreadContext context, IRubyObject object) {
return RubyString.objAsString(context, invokedynamic(context, object, INSPECT));
}
@Override
public IRubyObject rbClone() {
Ruby runtime = getRuntime();
if (isImmediate()) throw runtime.newTypeError("can't clone " + getMetaClass().getName());
// We're cloning ourselves, so we know the result should be a RubyObject
RubyBasicObject clone = (RubyBasicObject)getMetaClass().getRealClass().allocate();
clone.setMetaClass(getSingletonClassCloneAndAttach(clone));
if (isTaint()) clone.setTaint(true);
initCopy(runtime.getCurrentContext(), clone, this, true);
if (isFrozen()) clone.setFrozen(true);
return clone;
}
protected RubyClass getSingletonClassClone() {
return getSingletonClassCloneAndAttach(UNDEF);
}
/** rb_singleton_class_clone
*
* Will make sure that if the current objects class is a
* singleton, it will get cloned.
*
* @return either a real class, or a clone of the current singleton class
*/
protected RubyClass getSingletonClassCloneAndAttach(IRubyObject attach) {
RubyClass klass = getMetaClass();
if (!klass.isSingleton()) {
return klass;
}
RubyClass clone = new MetaClass(getRuntime(), klass.getSuperClass(), attach);
clone.flags = flags;
if (this instanceof RubyClass) {
clone.setMetaClass(clone);
} else {
clone.setMetaClass(klass.getSingletonClassClone());
}
if (klass.hasVariables()) {
clone.syncVariables(klass);
}
clone.syncConstants(klass);
klass.cloneMethods(clone);
((MetaClass) clone.getMetaClass()).setAttached(clone);
return clone;
}
/**
* Specifically polymorphic method that are meant to be overridden
* by modules to specify that they are modules in an easy way.
*/
@Override
public boolean isModule() {
return false;
}
/**
* Specifically polymorphic method that are meant to be overridden
* by classes to specify that they are classes in an easy way.
*/
@Override
public boolean isClass() {
return false;
}
/**
* @see org.jruby.runtime.builtin.IRubyObject#dataWrapStruct(Object)
*/
@Override
public synchronized void dataWrapStruct(Object obj) {
if (obj == null) {
removeInternalVariable("__wrap_struct__");
} else {
fastSetInternalVariable("__wrap_struct__", obj);
}
}
// The dataStruct is a place where custom information can be
// contained for core implementations that doesn't necessarily
// want to go to the trouble of creating a subclass of
// RubyObject. The OpenSSL implementation uses this heavily to
// save holder objects containing Java cryptography objects.
// Java integration uses this to store the Java object ref.
//protected transient Object dataStruct;
/**
* @see org.jruby.runtime.builtin.IRubyObject#dataGetStruct()
*/
@Override
public synchronized Object dataGetStruct() {
return getInternalVariable("__wrap_struct__");
}
// Equivalent of Data_Get_Struct
// This will first check that the object in question is actually a T_DATA equivalent.
@Override
public synchronized Object dataGetStructChecked() {
TypeConverter.checkData(this);
return getInternalVariable("__wrap_struct__");
}
/** rb_obj_id
*
* Return the internal id of an object.
*/
@JRubyMethod(name = "__id__")
@Override
public IRubyObject id() {
return getRuntime().newFixnum(getObjectId());
}
/**
* The logic here is to use the special objectId accessor slot from the
* parent as a lazy store for an object ID. IDs are generated atomically,
* in serial, and guaranteed unique for up to 2^63 objects. The special
* objectId slot is managed separately from the "normal" vars so it
* does not marshal, clone/dup, or refuse to be initially set when the
* object is frozen.
*/
protected long getObjectId() {
return metaClass.getRealClass().getVariableTableManager().getObjectId(this);
}
/** rb_obj_inspect
*
* call-seq:
* obj.inspect => string
*
* Returns a string containing a human-readable representation of
* obj. If not overridden, uses the to_s
method to
* generate the string.
*
* [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]"
* Time.new.inspect #=> "Wed Apr 09 08:54:39 CDT 2003"
*/
@Override
public IRubyObject inspect() {
if ((!isImmediate()) && !(this instanceof RubyModule) && hasVariables()) {
return hashyInspect();
}
if (isNil()) return RubyNil.inspect(getRuntime());
return to_s();
}
private static final byte[] INSPECT_POUND_LT = "#<".getBytes();
private static final byte[] INSPECT_COLON_ZERO_X = ":0x".getBytes();
private static final byte[] INSPECT_SPACE_DOT_DOT_DOT_GT = " ...>".getBytes();
private static final byte[] INSPECT_COMMA = ",".getBytes();
private static final byte[] INSPECT_SPACE = " ".getBytes();
private static final byte[] INSPECT_EQUALS = "=".getBytes();
private static final byte[] INSPECT_GT = ">".getBytes();
public final IRubyObject hashyInspect() {
final Ruby runtime = getRuntime();
byte[] name = getMetaClass().getRealClass().getName().getBytes(RubyEncoding.UTF8);
RubyString part = RubyString.newStringLight(runtime, 2 + name.length + 3 + 8 + 1); // #
encStrBufCat(runtime, part, INSPECT_POUND_LT);
encStrBufCat(runtime, part, name, UTF8Encoding.INSTANCE);
encStrBufCat(runtime, part, INSPECT_COLON_ZERO_X);
encStrBufCat(runtime, part, ConvertBytes.longToHexBytes(inspectHashCode()));
if (runtime.isInspecting(this)) {
encStrBufCat(runtime, part, INSPECT_SPACE_DOT_DOT_DOT_GT);
return part;
}
try {
runtime.registerInspecting(this);
return inspectObj(runtime, part);
} finally {
runtime.unregisterInspecting(this);
}
}
// MRI: rb_inspect, which does dispatch
public static IRubyObject rbInspect(ThreadContext context, IRubyObject obj) {
Ruby runtime = context.runtime;
RubyString str = sites(context).inspect.call(context, obj, obj).asString();
Encoding enc = runtime.getDefaultInternalEncoding();
if (enc == null) enc = runtime.getDefaultExternalEncoding();
if (!enc.isAsciiCompatible()) {
if (!str.isAsciiOnly()) {
return RubyString.rbStrEscape(context, str);
}
return str;
}
if (str.getEncoding() != enc && !str.isAsciiOnly()) {
return RubyString.rbStrEscape(context, str);
}
return str;
}
/**
* For most objects, the hash used in the default #inspect is just the
* identity hashcode of the actual object.
*
* See org.jruby.java.proxies.JavaProxy for a divergent case.
*
* @return The identity hashcode of this object
*/
protected int inspectHashCode() {
return System.identityHashCode(this);
}
/** inspect_obj
*
* The internal helper method that takes care of the part of the
* inspection that inspects instance variables.
*/
private RubyString inspectObj(final Ruby runtime, RubyString part) {
final ThreadContext context = runtime.getCurrentContext();
boolean first = true;
for (Map.Entry entry : metaClass.getVariableTableManager().getVariableAccessorsForRead().entrySet()) {
Object value = entry.getValue().get(this);
if (!(value instanceof IRubyObject) || !IdUtil.isInstanceVariable(entry.getKey())) continue;
IRubyObject obj = (IRubyObject) value;
if (!first) encStrBufCat(runtime, part, INSPECT_COMMA);
encStrBufCat(runtime, part, INSPECT_SPACE);
encStrBufCat(runtime, part, entry.getKey());
encStrBufCat(runtime, part, INSPECT_EQUALS);
encStrBufCat(runtime, part, sites(context).inspect.call(context, obj, obj).convertToString().getByteList());
first = false;
}
encStrBufCat(runtime, part, INSPECT_GT);
return part;
}
// Methods of the Object class (rb_obj_*):
@JRubyMethod(name = "!")
public IRubyObject op_not(ThreadContext context) {
return context.runtime.newBoolean(!this.isTrue());
}
/**
* The != method implemented for BasicObject. Note that this version is currently
* replaced by a Ruby version in basicobject.rb for better caching characteristics.
*
* @param context thread context
* @param other other object
* @return false if this == other, true otherwise
*/
@JRubyMethod(name = "!=", required = 1)
public IRubyObject op_not_equal(ThreadContext context, IRubyObject other) {
return context.runtime.newBoolean(!invokedynamic(context, this, OP_EQUAL, other).isTrue());
}
/**
* Compares this Ruby object with another.
*
* @param other another IRubyObject
* @return 0 if equal,
* < 0 if this is less than other,
* > 0 if this is greater than other
*/
@Override
public int compareTo(IRubyObject other) {
final Ruby runtime = getRuntime();
IRubyObject cmp = invokedynamic(runtime.getCurrentContext(), this, OP_CMP, other);
// if RubyBasicObject#op_cmp is used, the result may be nil (not comparable)
if ( ! cmp.isNil() ) {
return (int) cmp.convertToInteger().getLongValue();
}
/* We used to raise an error if two IRubyObject were not comparable, but
* in order to support the new ConcurrentHashMapV8 and other libraries
* and containers that arbitrarily call compareTo expecting it to always
* succeed, we have opted to return 0 here. This will allow all
* RubyBasicObject subclasses to be compared, but if the comparison is
* not valid we they will appear the same for sorting purposes.
*
* See https://jira.codehaus.org/browse/JRUBY-7013
*/
return 0;
}
/** rb_obj_equal
*
* Will by default use identity equality to compare objects. This
* follows the Ruby semantics.
*
* The name of this method doesn't follow the convention because hierarchy problems
*/
@Override
@JRubyMethod(name = "==")
public IRubyObject op_equal(ThreadContext context, IRubyObject obj) {
return this == obj ? context.runtime.getTrue() : context.runtime.getFalse();
}
@Deprecated
public IRubyObject op_equal_19(ThreadContext context, IRubyObject obj) {
return op_equal(context, obj);
}
@Override
public IRubyObject op_eqq(ThreadContext context, IRubyObject other) {
// Remain unimplemented due to problems with the double java hierarchy
return context.nil;
}
/**
* Helper method for checking equality, first using Java identity
* equality, and then calling the "==" method.
*/
protected static boolean equalInternal(final ThreadContext context, final IRubyObject that, final IRubyObject other){
return that == other || invokedynamic(context, that, OP_EQUAL, other).isTrue();
}
/** method used for Hash key comparison (specialized for String, Symbol and Fixnum)
*
* Will by default just call the Ruby method "eql?"
*/
@Override
public boolean eql(IRubyObject other) {
return invokedynamic(getRuntime().getCurrentContext(), this, EQL, other).isTrue();
}
/**
* Adds the specified object as a finalizer for this object.
*/
@Override
public void addFinalizer(IRubyObject f) {
Finalizer finalizer = (Finalizer)getInternalVariable("__finalizer__");
if (finalizer == null) {
// since this is the first time we're registering a finalizer, we
// must also register this object in ObjectSpace, so that future
// calls to undefine_finalizer, which takes an object ID, can
// locate the object properly. See JRUBY-4839.
long id = getObjectId();
IRubyObject fixnumId = id();
getRuntime().getObjectSpace().registerObjectId(id, this);
finalizer = new Finalizer(fixnumId);
fastSetInternalVariable("__finalizer__", finalizer);
getRuntime().addFinalizer(finalizer);
}
finalizer.addFinalizer(f);
}
/**
* Remove all the finalizers for this object.
*/
@Override
public void removeFinalizers() {
Finalizer finalizer = (Finalizer)getInternalVariable("__finalizer__");
if (finalizer != null) {
finalizer.removeFinalizers();
removeInternalVariable("__finalizer__");
getRuntime().removeFinalizer(finalizer);
}
}
@Override
public Object getVariable(int index) {
return VariableAccessor.getVariable(this, index);
}
@Override
public void setVariable(int index, Object value) {
ensureInstanceVariablesSettable();
if (index < 0) return;
metaClass.getVariableTableManager().setVariableInternal(this, index, value);
}
public final Object getFFIHandle() {
return metaClass.getVariableTableManager().getFFIHandle(this);
}
public final void setFFIHandle(Object value) {
metaClass.getVariableTableManager().setFFIHandle(this, value);
}
//
// COMMON VARIABLE METHODS
//
/**
* Returns true if object has any variables
*
* @see VariableTableManager#hasVariables(org.jruby.RubyBasicObject)
*/
@Override
public boolean hasVariables() {
return metaClass.getVariableTableManager().hasVariables(this);
}
/**
* Gets a list of all variables in this object.
*/
// TODO: must override in RubyModule to pick up constants
@Override
public List> getVariableList() {
Map ivarAccessors = metaClass.getVariableAccessorsForRead();
ArrayList> list = new ArrayList>();
for (Map.Entry entry : ivarAccessors.entrySet()) {
Object value = entry.getValue().get(this);
if (value == null) continue;
list.add(new VariableEntry
© 2015 - 2025 Weber Informatics LLC | Privacy Policy