/* **** 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) 2001 Alan Moore * Copyright (C) 2001 Chad Fowler * Copyright (C) 2001-2002 Benoit Cerrina * Copyright (C) 2001-2004 Jan Arne Petersen * Copyright (C) 2002-2004 Anders Bengtsson * Copyright (C) 2002-2005 Thomas E Enebo * Copyright (C) 2004-2005 Charles O Nutter * Copyright (C) 2004 Stefan Matthias Aust * Copyright (C) 2006 Ola Bini * Copyright (C) 2006 Daniel Steer * * 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 java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.RandomAccess; import java.util.stream.Stream; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; import org.jruby.api.Create; import org.jruby.api.JRubyAPI; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.Builtins; import org.jruby.runtime.CallSite; import org.jruby.runtime.ClassIndex; import org.jruby.runtime.JavaSites; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.callsite.CachingCallSite; import org.jruby.runtime.marshal.MarshalDumper; import org.jruby.runtime.marshal.MarshalLoader; import org.jruby.specialized.RubyArrayOneObject; import org.jruby.specialized.RubyArrayTwoObject; import org.jruby.util.TypeConverter; import org.jruby.util.collections.StringArraySet; import org.jruby.util.io.RubyInputStream; import org.jruby.util.io.RubyOutputStream; import static org.jruby.RubyEnumerator.SizeFn; import static org.jruby.api.Access.fixnumClass; import static org.jruby.api.Access.stringClass; import static org.jruby.api.Define.defineClass; import static org.jruby.api.Error.argumentError; import static org.jruby.api.Warn.warn; import static org.jruby.runtime.Visibility.PRIVATE; /** * The implementation of the built-in class Array in Ruby. * * Concurrency: no synchronization is required among readers, but * all users must synchronize externally with writers. * * Note: elt(long) is based on notion if we exceed precision by passing in a long larger * than int (actually Integer.MAX - 8) then it will just return nil. Anything using eltOk * must know this already to avoid an AIOOBE. We do catch that in eltOk but only as an * attempt at detecting concurrent modifications to the length. So if you improperly * use eltOk you will get a conc error and not AIOOBE. * * Guidance for elt(long) is that if the Ruby method should raise if it is too large * then you need to do that before you call this (toLong vs getValue/asLong). * * @param What array holds * */ @JRubyClass(name="Array", include = { "Enumerable" }, overrides = {RubyArrayNative.class, RubyArrayOneObject.class, RubyArrayTwoObject.class, StringArraySet.class}) public abstract class RubyArray extends RubyObject implements List, RandomAccess { public static final int DEFAULT_INSPECT_STR_SIZE = 10; public static RubyClass createArrayClass(ThreadContext context, RubyClass Object, RubyModule Enumerable) { return defineClass(context, "Array", Object, RubyArray::newEmptyArray). reifiedClass(RubyArray.class). kindOf(new RubyModule.JavaClassKindOf(RubyArray.class)). classIndex(ClassIndex.ARRAY). include(context, Enumerable). defineMethods(context, RubyArray.class); } @Override public ClassIndex getNativeClassIndex() { return ClassIndex.ARRAY; } @Deprecated(since = "10.0.0.0") public static IRubyObject create(IRubyObject klass, IRubyObject[] args, Block block) { return create(klass.getRuntime().getCurrentContext(), klass, args, block); } @JRubyMethod(name = "[]", rest = true, meta = true) public static IRubyObject create(ThreadContext context, IRubyObject klass, IRubyObject[] args, Block block) { return RubyArrayNative.create(context, klass, args, block); } protected RubyArray(Ruby runtime, RubyClass klass) { super(runtime, klass); } protected RubyArray(RubyClass klass) { super(klass.runtime, klass); } protected RubyArray(Ruby runtime, RubyClass klass, boolean objectSpace) { super(runtime, klass, objectSpace); } public static RubyArray newArray(final Ruby runtime, final int len) { return RubyArrayNative.newArray(runtime, len); } public static RubyArray newArray(ThreadContext context, final int len) { return RubyArrayNative.newArray(context, len); } public static RubyArray newArrayLight(final Ruby runtime, final int len) { return RubyArrayNative.newArrayLight(runtime, len); } @Deprecated(since = "10.0.0.0") public static final RubyArray newArray(final Ruby runtime) { return newArray(runtime.getCurrentContext()); } public static RubyArray newArray(ThreadContext context) { return RubyArrayNative.newArray(context); } public static RubyArray newArrayLight(final Ruby runtime) { return RubyArrayNative.newArrayLight(runtime); } public static RubyArray newArray(Ruby runtime, IRubyObject obj) { return RubyArrayNative.newArray(runtime, obj); } public static RubyArray newArrayLight(Ruby runtime, IRubyObject obj) { return RubyArrayNative.newArrayLight(runtime, obj); } public static RubyArray newArrayLight(RubyClass arrayClass, IRubyObject obj) { return RubyArrayNative.newArrayLight(arrayClass, obj); } public static RubyArray newArrayLight(Ruby runtime, IRubyObject car, IRubyObject cdr) { return RubyArrayNative.newArrayLight(runtime, car, cdr); } public static RubyArray newArrayLight(RubyClass arrayClass, IRubyObject car, IRubyObject cdr) { return RubyArrayNative.newArrayLight(arrayClass, car, cdr); } public static RubyArray newArrayLight(Ruby runtime, IRubyObject... objs) { return RubyArrayNative.newArrayLight(runtime, objs); } public static RubyArray newArray(Ruby runtime, IRubyObject car, IRubyObject cdr) { return RubyArrayNative.newArray(runtime, car, cdr); } public static RubyArray newArray(Ruby runtime, IRubyObject first, IRubyObject second, IRubyObject third) { return RubyArrayNative.newArray(runtime, first, second, third); } public static RubyArray newArray(Ruby runtime, IRubyObject first, IRubyObject second, IRubyObject third, IRubyObject fourth) { return RubyArrayNative.newArray(runtime, first, second, third, fourth); } public static RubyArray newEmptyArray(Ruby runtime) { return RubyArrayNative.newEmptyArray(runtime); } public static RubyArray newEmptyArray(Ruby runtime, RubyClass klass) { return RubyArrayNative.newEmptyArray(runtime, klass); } public static RubyArray newArray(Ruby runtime, IRubyObject[] args) { return RubyArrayNative.newArray(runtime, args); } public static RubyArray newArray(Ruby runtime, Collection collection) { return RubyArrayNative.newArray(runtime, collection); } public static RubyArray newArray(Ruby runtime, List list) { return RubyArrayNative.newArray(runtime, list); } public static RubyArray newSharedArray(RubyClass arrayClass, IRubyObject[] shared) { return RubyArrayNative.newSharedArray(arrayClass, shared); } public static RubyArray newArrayMayCopy(Ruby runtime, IRubyObject... args) { return RubyArrayNative.newArrayMayCopy(runtime, args); } public static RubyArray newArrayMayCopy(Ruby runtime, IRubyObject[] args, int start) { return RubyArrayNative.newArrayMayCopy(runtime, args, start); } /** * Construct a new RubyArray given the specified range of elements in the source array. The elements * may be copied into a new backing store, and therefore you should not expect future changes to the * source array to be reflected. Conversely, you should not modify the array after passing it, since * the contents may not be copied. * * @param runtime the runtime * @param args the args * @param start start index * @param length number of elements * @return an array referencing the given elements */ public static RubyArray newArrayMayCopy(Ruby runtime, IRubyObject[] args, int start, int length) { return RubyArrayNative.newArrayMayCopy(runtime, args, start, length); } public static RubyArray newArrayNoCopy(Ruby runtime, IRubyObject... args) { return RubyArrayNative.newArrayNoCopy(runtime, args); } public static RubyArray newArrayNoCopy(Ruby runtime, IRubyObject[] args, int begin) { return RubyArrayNative.newArrayNoCopy(runtime, args, begin); } public static RubyArray newArrayNoCopy(Ruby runtime, IRubyObject[] args, int begin, int length) { return RubyArrayNative.newArrayNoCopy(runtime, args, begin, length); } public static RubyArray newArrayNoCopyLight(Ruby runtime, IRubyObject[] args) { return RubyArrayNative.newArrayNoCopyLight(runtime, args); } @Deprecated(since = "10.0.0.0") protected static final void checkLength(Ruby runtime, long length) { checkLength(runtime.getCurrentContext(), length); } public static final int checkLength(ThreadContext context, long length) { if (length < 0) throw argumentError(context, "negative array size (or size too big)"); if (length >= Integer.MAX_VALUE) throw argumentError(context, "array size too big"); return (int) length; } /** * @deprecated RubyArray implements List, use it directly * @return a read-only copy of this list */ @Deprecated(since = "9.4-") public final List getList() { return Arrays.asList(toJavaArray()); } public abstract int getLength(); /** * @return "" * @deprecated Use {@link RubyArray#toJavaArray(ThreadContext)} instead. */ @Deprecated(since = "10.0.0.0") public IRubyObject[] toJavaArray() { return toJavaArray(getCurrentContext()); } /** * Return a Java array copy of the elements contained in this Array. * * This version always creates a new Java array that is exactly the length of the Array's elements. * * @return a Java array with exactly the size and contents of this RubyArray's elements */ public abstract IRubyObject[] toJavaArray(ThreadContext context); /** * Return a reference to this RubyArray's underlying Java array, if it is not shared with another RubyArray, or * an exact copy of the relevant range otherwise. * * This method is typically used to work with the underlying array directly, knowing that it is not shared and that * all accesses must consider the begin offset. * * @return The underlying Java array for this RubyArray, or a copy if that array is shared. */ public abstract IRubyObject[] toJavaArrayUnsafe(); /** * Return a Java array of the elements contained in this array, possibly a new array object. * * Use this method to potentially avoid making a new array and copying elements when the Array does not view a * subset of the underlying Java array. * * @return a Java array with exactly the size and contents of this RubyArray's elements, possibly the actual * underlying array. */ public abstract IRubyObject[] toJavaArrayMaybeUnsafe(); public abstract boolean isSharedJavaArray(RubyArray other); @JRubyMethod(visibility = PRIVATE) public abstract IRubyObject initialize(ThreadContext context, Block block); @JRubyMethod(visibility = PRIVATE) public abstract IRubyObject initialize(ThreadContext context, IRubyObject arg0, Block block); @JRubyMethod(visibility = PRIVATE) public abstract IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block); @JRubyMethod(name = {"initialize_copy"}, visibility=PRIVATE) public abstract IRubyObject initialize_copy(ThreadContext context, IRubyObject orig); public abstract RubyArray aryDup(); @Deprecated(since = "10.0.0.0") public IRubyObject replace(IRubyObject orig) { return replace(getCurrentContext(), orig); } @JRubyMethod(name = {"replace"}) public abstract IRubyObject replace(ThreadContext context, IRubyObject orig); @Override public abstract RubyString to_s(ThreadContext context); public abstract boolean includes(ThreadContext context, IRubyObject item); protected abstract boolean includesByEql(ThreadContext context, IRubyObject item); @JRubyMethod(name = "hash") public abstract RubyFixnum hash(ThreadContext context); public abstract IRubyObject store(long index, IRubyObject value); /** * Store an element at the specified index or throw if the index is invalid. * @param context the current thread context * @param index the offset to store the value * @param value the value to be stored * @return the value set */ @JRubyAPI public abstract IRubyObject store(ThreadContext context, long index, IRubyObject value); // note: packed arrays will unpack in overridden version of this protected abstract void storeInternal(ThreadContext context, int index, IRubyObject value); protected abstract IRubyObject elt(long offset); public abstract T eltOk(long offset); public abstract T eltSetOk(long offset, T value); public abstract T eltSetOk(int offset, T value); public abstract IRubyObject entry(long offset); public abstract IRubyObject entry(int offset); public abstract T eltInternal(int offset); public abstract T eltInternalSet(int offset, T item); /** * Variable arity version for compatibility. Not bound to a Ruby method. * @deprecated Use the versions with zero, one, or two args. */ @Deprecated(since = "9.4-") public IRubyObject fetch(ThreadContext context, IRubyObject[] args, Block block) { switch (args.length) { case 1: return fetch(context, args[0], block); case 2: return fetch(context, args[0], args[1], block); default: Arity.raiseArgumentError(context, args.length, 1, 2); return null; // not reached } } @JRubyMethod(rest = true) public abstract IRubyObject fetch_values(ThreadContext context, IRubyObject[] args, Block block); @JRubyMethod public abstract IRubyObject fetch(ThreadContext context, IRubyObject arg0, Block block); @JRubyMethod public abstract IRubyObject fetch(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block); public static RubyArray aryToAry(ThreadContext context, IRubyObject obj) { IRubyObject tmp = TypeConverter.checkArrayType(context, obj); return tmp != context.nil ? (RubyArray) tmp : RubyArray.newArray(context.runtime, obj); } @Deprecated(since = "10.0.0.0") public IRubyObject insert(IRubyObject arg) { return insert(getCurrentContext(), arg); } @JRubyMethod(name = "insert") public abstract IRubyObject insert(ThreadContext context, IRubyObject arg); @Deprecated(since = "10.0.0.0") public IRubyObject insert(IRubyObject arg1, IRubyObject arg2) { return insert(getCurrentContext(), arg1, arg2); } @JRubyMethod(name = "insert") public abstract IRubyObject insert(ThreadContext context, IRubyObject arg1, IRubyObject arg2); @Deprecated(since = "10.0.0.0") public IRubyObject insert(IRubyObject[] args) { return insert(getCurrentContext(), args); } @JRubyMethod(name = "insert", required = 1, rest = true, checkArity = false) public abstract IRubyObject insert(ThreadContext context, IRubyObject[] args); @Deprecated(since = "10.0.0.0") public RubyArray transpose() { return transpose(getCurrentContext()); } @JRubyMethod(name = "transpose") public abstract RubyArray transpose(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject values_at(IRubyObject[] args) { return values_at(getCurrentContext(), args); } @JRubyMethod(name = "values_at", rest = true) public abstract IRubyObject values_at(ThreadContext context, IRubyObject[] args); public abstract IRubyObject subseqLight(long beg, long len); public abstract IRubyObject subseq(RubyClass metaClass, long beg, long len, boolean light); @JRubyMethod(name = "length", alias = "size") public abstract RubyFixnum length(ThreadContext context); @Deprecated(since = "10.0.0.0") public RubyFixnum length() { return length(getCurrentContext()); } /** * A size method suitable for lambda method reference implementation of {@link SizeFn#size(ThreadContext, IRubyObject, IRubyObject[])} * * @see SizeFn#size(ThreadContext, IRubyObject, IRubyObject[]) */ protected static IRubyObject size(ThreadContext context, RubyArray self, IRubyObject[] args) { return self.length(context); } @Deprecated(since = "10.0.0.0") public RubyArray append(IRubyObject item) { return append(getCurrentContext(), item); } @JRubyMethod(name = "<<") public abstract RubyArray append(ThreadContext context, IRubyObject item); @Deprecated(since = "10.0.3.0") // not-used public RubyArray push_m(IRubyObject[] items) { return push(items); } @Deprecated(since = "10.0.0.0") public RubyArray push(IRubyObject item) { append(item); return this; } @JRubyMethod(name = "push", alias = "append") public abstract RubyArray push(ThreadContext context, IRubyObject item); @Deprecated(since = "10.0.0.0") public RubyArray push(IRubyObject[] items) { return push(getCurrentContext(), items); } @JRubyMethod(name = "push", alias = "append", rest = true) public abstract RubyArray push(ThreadContext context, IRubyObject[] items); @JRubyMethod public abstract IRubyObject pop(ThreadContext context); @JRubyMethod public abstract IRubyObject pop(ThreadContext context, IRubyObject num); @JRubyMethod(name = "shift") public abstract IRubyObject shift(ThreadContext context); @JRubyMethod(name = "shift") public abstract IRubyObject shift(ThreadContext context, IRubyObject num); @Deprecated(since = "10.0.0.0") public IRubyObject unshift() { return unshift(getCurrentContext()); } @JRubyMethod(name = "unshift", alias = "prepend") public abstract IRubyObject unshift(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject unshift(IRubyObject item) { return unshift(getCurrentContext(), item); } @JRubyMethod(name = "unshift", alias = "prepend") public abstract IRubyObject unshift(ThreadContext context, IRubyObject item); @Deprecated(since = "10.0.0.0") public IRubyObject unshift(IRubyObject[] items) { return unshift(getCurrentContext(), items); } @JRubyMethod(name = "unshift", alias = "prepend", rest = true) public abstract IRubyObject unshift(ThreadContext context, IRubyObject[] items); @JRubyMethod(name = "include?") public abstract RubyBoolean include_p(ThreadContext context, IRubyObject item); /** * Variable arity version for compatibility. Not bound to a Ruby method. * @deprecated Use the versions with zero, one, or two args. */ @Deprecated(since = "10.0.0.0") public IRubyObject aref(IRubyObject[] args) { ThreadContext context = getCurrentContext(); return switch (args.length) { case 1 -> aref(context, args[0]); case 2 -> aref(context, args[0], args[1]); default -> { Arity.raiseArgumentError(context, args.length, 1, 2); yield null; } }; } @JRubyMethod(name = {"[]", "slice"}) public abstract IRubyObject aref(ThreadContext context, IRubyObject arg0); @Deprecated(since = "10.0.0.0") public IRubyObject aref(IRubyObject arg0, IRubyObject arg1) { return aref(getCurrentContext(), arg0, arg1); } @JRubyMethod(name = {"[]", "slice"}) public abstract IRubyObject aref(ThreadContext context, IRubyObject arg0, IRubyObject arg1); /** * Variable arity version for compatibility. Not bound to a Ruby method. * @deprecated Use the versions with zero, one, or two args. */ @Deprecated(since = "9.4-") public IRubyObject aset(IRubyObject[] args) { return switch (args.length) { case 2 -> aset(args[0], args[1]); case 3 -> aset(args[0], args[1], args[2]); default -> throw argumentError(getCurrentContext(), "wrong number of arguments (" + args.length + " for 2)"); }; } @Deprecated(since = "10.0.0.0") public IRubyObject aset(IRubyObject arg0, IRubyObject arg1) { return aset(getCurrentContext(), arg0, arg1); } /** * @param arg0 * @param arg1 * @param arg2 * @return "" * @deprecated Use {@link RubyArray#aset(ThreadContext, IRubyObject, IRubyObject, IRubyObject)} instead. */ @Deprecated(since = "10.0.0.0") public IRubyObject aset(IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) { return aset(getCurrentContext(), arg0, arg1, arg2); } @JRubyMethod(name = "[]=") public abstract IRubyObject aset(ThreadContext context, IRubyObject arg0, IRubyObject arg1); @JRubyMethod(name = "[]=") public abstract IRubyObject aset(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2); /** * @param pos * @return "" * @deprecated Use {@link RubyArray#at(ThreadContext, IRubyObject)} instead. */ @Deprecated(since = "10.0.0.0") public IRubyObject at(IRubyObject pos) { return at(getCurrentContext(), pos); } @JRubyMethod(name = "at") public abstract IRubyObject at(ThreadContext context, IRubyObject pos); @JRubyMethod(name = "concat") public abstract RubyArray concat(ThreadContext context, IRubyObject obj); @Deprecated(since = "10.0.0.0") public RubyArray aryAppend(RubyArray y) { return aryAppend(getCurrentContext(), y); } public abstract RubyArray aryAppend(ThreadContext context, RubyArray y); @JRubyMethod(name = "concat", rest = true) public abstract RubyArray concat(ThreadContext context, IRubyObject[] objs); public abstract RubyArray concat(IRubyObject obj); @JRubyMethod(name = "inspect", alias = "to_s") public abstract RubyString inspect(ThreadContext context); @Deprecated(since = "9.4-") public IRubyObject first(IRubyObject[] args) { return switch (args.length) { case 0 -> first(getCurrentContext()); case 1 -> first(getCurrentContext(), args[0]); default -> { Arity.raiseArgumentError(getCurrentContext(), args.length, 0, 1); yield null; } }; } @Deprecated(since = "10.0.0.0") public IRubyObject first() { return first(getCurrentContext()); } @JRubyAPI @JRubyMethod(name = "first") public abstract IRubyObject first(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject first(IRubyObject arg0) { return first(getCurrentContext(), arg0); } @JRubyAPI @JRubyMethod(name = "first") public abstract IRubyObject first(ThreadContext context, IRubyObject arg0); /** * Variable arity version for compatibility. Not bound to a Ruby method. * @deprecated Use the versions with zero, one, or two args. */ @Deprecated(since = "9.4-") public IRubyObject last(IRubyObject[] args) { return switch (args.length) { case 0 -> last(getCurrentContext()); case 1 -> last(getCurrentContext(), args[0]); default -> { Arity.raiseArgumentError(getCurrentContext(), args.length, 0, 1); yield null; } }; } @Deprecated(since = "10.0.0.0") public IRubyObject last() { return last(getCurrentContext()); } @JRubyAPI @JRubyMethod(name = "last") public abstract IRubyObject last(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject last(IRubyObject arg0) { return last(getCurrentContext(), arg0); } @JRubyMethod(name = "last") public abstract IRubyObject last(ThreadContext context, IRubyObject arg0); @JRubyMethod public abstract IRubyObject each(ThreadContext context, Block block); public abstract IRubyObject eachSlice(ThreadContext context, int size, Block block); @JRubyMethod public abstract IRubyObject each_slice(ThreadContext context, IRubyObject arg, Block block); @JRubyMethod public abstract IRubyObject each_index(ThreadContext context, Block block); @JRubyMethod public abstract IRubyObject reverse_each(ThreadContext context, Block block); @Deprecated(since = "10.0.0.0") public IRubyObject join19(final ThreadContext context, IRubyObject sep) { return join(context, sep); } @JRubyMethod(name = "join") public abstract IRubyObject join(final ThreadContext context, IRubyObject sep); @Deprecated(since = "10.0.0.0") public IRubyObject join19(ThreadContext context) { return join(context); } @JRubyMethod(name = "join") public abstract IRubyObject join(ThreadContext context); @JRubyMethod(name = "to_a") @Override public abstract RubyArray to_a(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject to_ary() { return this; } @JRubyMethod(name = "to_ary") public final IRubyObject to_ary(ThreadContext context) { return this; } @JRubyMethod(name = "to_h") public abstract IRubyObject to_h(ThreadContext context, Block block); @Override public final RubyArray convertToArray() { return this; } @Override public final IRubyObject checkArrayType(){ return this; } @JRubyMethod(name = "==") @Override public abstract IRubyObject op_equal(ThreadContext context, IRubyObject obj); public abstract RubyBoolean compare(ThreadContext context, CallSite site, IRubyObject other); @JRubyMethod(name = "eql?") public abstract IRubyObject eql(ThreadContext context, IRubyObject obj); /** * @return "" * @deprecated Use {@link RubyArray#compact_bang(ThreadContext)} */ @Deprecated(since = "10.0.0.0") public IRubyObject compact_bang() { return compact_bang(getCurrentContext()); } @JRubyMethod(name = "compact!") public abstract IRubyObject compact_bang(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject compact() { return compact(getCurrentContext()); } @JRubyMethod(name = "compact") public abstract IRubyObject compact(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject empty_p() { return empty_p(getCurrentContext()); } @JRubyMethod(name = "empty?") public abstract IRubyObject empty_p(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject rb_clear() { return rb_clear(getCurrentContext()); } @JRubyMethod(name = "clear") public abstract IRubyObject rb_clear(ThreadContext context); @JRubyMethod public abstract IRubyObject fill(ThreadContext context, Block block); @JRubyMethod public abstract IRubyObject fill(ThreadContext context, IRubyObject arg, Block block); @JRubyMethod public abstract IRubyObject fill(ThreadContext context, IRubyObject arg1, IRubyObject arg2, Block block); @JRubyMethod public abstract IRubyObject fill(ThreadContext context, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, Block block); public abstract IRubyObject index(ThreadContext context, IRubyObject obj); @JRubyMethod(name = {"index", "find_index"}) public abstract IRubyObject index(ThreadContext context, IRubyObject obj, Block unused); @JRubyMethod(name = {"index", "find_index"}) public abstract IRubyObject index(ThreadContext context, Block block); @JRubyMethod public abstract IRubyObject bsearch(ThreadContext context, Block block); @JRubyMethod public abstract IRubyObject bsearch_index(ThreadContext context, Block block); @JRubyMethod public abstract IRubyObject rindex(ThreadContext context, IRubyObject obj, Block unused); @JRubyMethod public abstract IRubyObject rindex(ThreadContext context, Block block); @Deprecated(since = "10.0.0.0") public IRubyObject indexes(IRubyObject[] args) { return indexes(getCurrentContext(), args); } @Deprecated(since = "10.0.0.0") public IRubyObject indexes(ThreadContext context, IRubyObject[] args) { int argc = Arity.checkArgumentCount(context, args, 1, -1); warn(context, "Array#indexes is deprecated; use Array#values_at"); if (argc == 1) return Create.newArray(context, args[0]); RubyArrayNative ary = RubyArrayNative.newBlankArrayInternal(context.runtime, argc); for (int i = 0; i < argc; i++) { ary.storeInternal(context, i, aref(context, args[i])); } ary.realLength = argc; return ary; } @Deprecated(since = "10.0.0.0") public IRubyObject reverse_bang() { return reverse_bang(getCurrentContext()); } @JRubyMethod(name = "reverse!") public abstract IRubyObject reverse_bang(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject reverse() { return reverse(getCurrentContext()); } @JRubyAPI @JRubyMethod(name = "reverse") public abstract IRubyObject reverse(ThreadContext context); @JRubyMethod(name = {"collect"}) public abstract IRubyObject rbCollect(ThreadContext context, Block block); @JRubyMethod(name = {"map"}) public abstract IRubyObject map(ThreadContext context, Block block); @JRubyMethod(name = "collect!") public abstract IRubyObject collect_bang(ThreadContext context, Block block); @JRubyMethod(name = "map!") public abstract IRubyObject map_bang(ThreadContext context, Block block); @JRubyMethod(name = "select", alias = "filter") public abstract IRubyObject select(ThreadContext context, Block block); @JRubyMethod(name = "select!", alias = "filter!") public abstract IRubyObject select_bang(ThreadContext context, Block block); @JRubyMethod public abstract IRubyObject keep_if(ThreadContext context, Block block); @JRubyMethod public final IRubyObject deconstruct(ThreadContext context) { return this; } @JRubyMethod public abstract IRubyObject delete(ThreadContext context, IRubyObject item, Block block); @Deprecated(since = "10.0.0.0") public IRubyObject delete_at(int pos) { return delete_at(getCurrentContext(), pos); } public abstract IRubyObject delete_at(ThreadContext context, int pos); @Deprecated(since = "10.0.0.0") public IRubyObject delete_at(IRubyObject obj) { return delete_at(getCurrentContext(), obj); } @JRubyMethod(name = "delete_at") public abstract IRubyObject delete_at(ThreadContext context, IRubyObject obj); @JRubyMethod public abstract IRubyObject reject(ThreadContext context, Block block); @JRubyMethod(name = "reject!") public abstract IRubyObject reject_bang(ThreadContext context, Block block); @JRubyMethod public abstract IRubyObject delete_if(ThreadContext context, Block block); @JRubyMethod(optional = 1, rest = true, checkArity = false) public abstract IRubyObject zip(ThreadContext context, IRubyObject[] args, Block block); // This can be shared with RubyEnumerable to clean #zipCommon{Enum,Arg} a little public static interface ArgumentVisitor { IRubyObject visit(ThreadContext ctx, IRubyObject arg, int i); } @JRubyMethod(name = "<=>") public abstract IRubyObject op_cmp(ThreadContext context, IRubyObject obj); /** * Variable arity version for compatibility. Not bound to a Ruby method. * @deprecated Use the versions with zero, one, or two args. */ @Deprecated(since = "9.4-") public IRubyObject slice_bang(IRubyObject[] args) { switch (args.length) { case 1: return slice_bang(args[0]); case 2: return slice_bang(args[0], args[1]); default: Arity.raiseArgumentError(getCurrentContext(), args.length, 1, 2); return null; // not reached } } @Deprecated(since = "10.0.0.0") public IRubyObject slice_bang(IRubyObject arg0) { return slice_bang(getCurrentContext(), arg0); } @JRubyMethod(name = "slice!") public abstract IRubyObject slice_bang(ThreadContext context, IRubyObject arg0); @Deprecated(since = "10.0.0.0") public IRubyObject slice_bang(IRubyObject arg0, IRubyObject arg1) { return slice_bang(getCurrentContext(), arg0, arg1); } @JRubyMethod(name = "slice!") public abstract IRubyObject slice_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1); @JRubyMethod(name = "assoc") public abstract IRubyObject assoc(ThreadContext context, IRubyObject key); @JRubyMethod(name = "rassoc") public abstract IRubyObject rassoc(ThreadContext context, IRubyObject value); @JRubyMethod(name = "flatten!") public abstract IRubyObject flatten_bang(ThreadContext context); @JRubyMethod(name = "flatten!") public abstract IRubyObject flatten_bang(ThreadContext context, IRubyObject arg); @JRubyMethod(name = "flatten") public abstract IRubyObject flatten(ThreadContext context); @JRubyMethod(name = "flatten") public abstract IRubyObject flatten(ThreadContext context, IRubyObject arg); @JRubyMethod(name = "count") public abstract IRubyObject count(ThreadContext context, Block block); @JRubyMethod(name = "count") public abstract IRubyObject count(ThreadContext context, IRubyObject obj, Block block); @JRubyMethod(name = "nitems") public abstract IRubyObject nitems(ThreadContext context); @Deprecated(since = "10.0.0.0") public IRubyObject nitems() { return nitems(getCurrentContext()); } @Deprecated(since = "10.0.0.0") public IRubyObject op_plus(IRubyObject obj) { return op_plus(getCurrentContext(), obj); } @JRubyMethod(name = "+") public abstract IRubyObject op_plus(ThreadContext context, IRubyObject obj); @JRubyMethod(name = "*") public abstract IRubyObject op_times(ThreadContext context, IRubyObject times); protected abstract RubyHash makeHash(Ruby runtime); protected abstract RubyHash makeHash(RubyHash hash); @JRubyMethod(name = "uniq!") public abstract IRubyObject uniq_bang(ThreadContext context, Block block); @JRubyMethod(name = "uniq") public abstract IRubyObject uniq(ThreadContext context, Block block); @Deprecated(since = "10.0.0.0") public IRubyObject op_diff(IRubyObject other) { return op_diff(getCurrentContext(), other); } @JRubyMethod(name = "-") public abstract IRubyObject op_diff(ThreadContext context, IRubyObject other); @JRubyMethod(name = "difference", rest = true) public abstract IRubyObject difference(ThreadContext context, IRubyObject[] args); @JRubyMethod(rest = true) public abstract IRubyObject intersection(ThreadContext context, IRubyObject[] args); @JRubyMethod(name = "intersect?") public abstract IRubyObject intersect_p(ThreadContext context, IRubyObject other); @Deprecated(since = "10.0.0.0") public IRubyObject op_and(IRubyObject other) { return op_and(getCurrentContext(), other); } @JRubyMethod(name = "&") public abstract IRubyObject op_and(ThreadContext context, IRubyObject other); @Deprecated(since = "10.0.0.0") public IRubyObject op_or(IRubyObject other) { return op_or(getCurrentContext(), other); } @JRubyMethod(name = "|") public abstract IRubyObject op_or(ThreadContext context, IRubyObject other); @JRubyMethod(name = "union", rest = true) public abstract IRubyObject union(ThreadContext context, IRubyObject[] args); protected abstract void unionInternal(ThreadContext context, RubyArray... args); @JRubyMethod(name = "sort") public abstract RubyArray sort(ThreadContext context, Block block); @JRubyMethod(name = "sort!") public abstract IRubyObject sort_bang(ThreadContext context, Block block); public static class DefaultComparator implements Comparator { final ThreadContext context; private final boolean fixnumBypass; private final boolean stringBypass; public DefaultComparator(ThreadContext context) { this(context, true); } DefaultComparator(ThreadContext context, final boolean honorOverride) { this.context = context; if ( honorOverride && context != null ) { this.fixnumBypass = !honorOverride || Builtins.checkIntegerCmp(context); this.stringBypass = !honorOverride || Builtins.checkStringCmp(context); } else { // no-opt this.fixnumBypass = false; this.stringBypass = false; } } /* DefaultComparator(ThreadContext context, final boolean fixnumBypass, final boolean stringBypass) { this.context = context; this.fixnumBypass = fixnumBypass; this.stringBypass = stringBypass; } */ public int compare(IRubyObject obj1, IRubyObject obj2) { if (fixnumBypass && obj1 instanceof RubyFixnum fix1 && obj2 instanceof RubyFixnum fix2) { return compareInteger(fix1, fix2); } if (stringBypass && obj1 instanceof RubyString str1 && obj2 instanceof RubyString str2) { return compareString(str1, str2); } return compareGeneric(obj1, obj2); } protected int compareGeneric(IRubyObject o1, IRubyObject o2) { final ThreadContext context = context(); return compareGeneric(context, sites(context).op_cmp_sort, o1, o2); } protected ThreadContext context() { return context; } public static int compareInteger(RubyFixnum o1, RubyFixnum o2) { long a = o1.getValue(); long b = o2.getValue(); return a > b ? 1 : a == b ? 0 : -1; } public static int compareString(RubyString o1, RubyString o2) { return o1.op_cmp(o2); } public static int compareGeneric(ThreadContext context, IRubyObject o1, IRubyObject o2) { return compareGeneric(context, sites(context).op_cmp_sort, o1, o2); } public static int compareGeneric(ThreadContext context, CallSite op_cmp_sort, IRubyObject o1, IRubyObject o2) { IRubyObject ret = op_cmp_sort.call(context, o1, o1, o2); return RubyComparable.cmpint(context, ret, o1, o2); } } @JRubyMethod(name = "sort_by!") public abstract IRubyObject sort_by_bang(ThreadContext context, Block block); @JRubyMethod(name = "take") public abstract IRubyObject take(ThreadContext context, IRubyObject n); @JRubyMethod(name = "take_while") public abstract IRubyObject take_while(ThreadContext context, Block block); @JRubyMethod(name = "drop") public abstract IRubyObject drop(ThreadContext context, IRubyObject n); @JRubyMethod(name = "drop_while") public abstract IRubyObject drop_while(ThreadContext context, Block block); @JRubyMethod(name = "cycle") public abstract IRubyObject cycle(ThreadContext context, Block block); @JRubyMethod(name = "cycle") public abstract IRubyObject cycle(ThreadContext context, IRubyObject arg, Block block); @JRubyMethod(name = "product", rest = true) public abstract IRubyObject product(ThreadContext context, IRubyObject[] args, Block block); @JRubyMethod(name = "combination") public abstract IRubyObject combination(ThreadContext context, IRubyObject num, Block block); @JRubyMethod(name = "repeated_combination") public abstract IRubyObject repeatedCombination(ThreadContext context, IRubyObject num, Block block); @JRubyMethod(name = "permutation") public abstract IRubyObject permutation(ThreadContext context, IRubyObject num, Block block); @JRubyMethod(name = "permutation") public abstract IRubyObject permutation(ThreadContext context, Block block); @JRubyMethod(name = "repeated_permutation") public abstract IRubyObject repeated_permutation(ThreadContext context, IRubyObject num, Block block); @JRubyMethod(name = "shuffle!") public abstract IRubyObject shuffle_bang(ThreadContext context); @JRubyMethod(name = "shuffle!") public abstract IRubyObject shuffle_bang(ThreadContext context, IRubyObject opts); @JRubyMethod(name = "shuffle") public abstract IRubyObject shuffle(ThreadContext context); @JRubyMethod(name = "shuffle") public abstract IRubyObject shuffle(ThreadContext context, IRubyObject opts); @JRubyMethod(name = "sample") public abstract IRubyObject sample(ThreadContext context); @JRubyMethod(name = "sample") public abstract IRubyObject sample(ThreadContext context, IRubyObject sampleOrOpts); @JRubyMethod(name = "sample") public abstract IRubyObject sample(ThreadContext context, IRubyObject sample, IRubyObject opts); @JRubyMethod(name = "rotate!") public abstract IRubyObject rotate_bang(ThreadContext context); @JRubyMethod(name = "rotate!") public abstract IRubyObject rotate_bang(ThreadContext context, IRubyObject cnt); @JRubyMethod(name = "rotate") public abstract IRubyObject rotate(ThreadContext context); @JRubyMethod(name = "rotate") public abstract IRubyObject rotate(ThreadContext context, IRubyObject cnt); @JRubyMethod(name = "all?") public abstract IRubyObject all_p(ThreadContext context, Block block); @JRubyMethod(name = "all?") public abstract IRubyObject all_p(ThreadContext context, IRubyObject arg, Block block); @JRubyMethod(name = "any?") public abstract IRubyObject any_p(ThreadContext context, Block block); @JRubyMethod(name = "any?") public abstract IRubyObject any_p(ThreadContext context, IRubyObject arg, Block block); @JRubyMethod(name = "none?") public abstract IRubyObject none_p(ThreadContext context, Block block); @JRubyMethod(name = "none?") public abstract IRubyObject none_p(ThreadContext context, IRubyObject arg, Block block); @JRubyMethod(name = "one?") public abstract IRubyObject one_p(ThreadContext context, Block block); @JRubyMethod(name = "one?") public abstract IRubyObject one_p(ThreadContext context, IRubyObject arg, Block block); @JRubyMethod public abstract IRubyObject sum(final ThreadContext context, final Block block); @JRubyMethod public abstract IRubyObject sum(final ThreadContext context, IRubyObject init, final Block block); public abstract IRubyObject find(ThreadContext context, IRubyObject ifnone, Block block); public abstract IRubyObject find_index(ThreadContext context, Block block); public abstract IRubyObject find_index(ThreadContext context, IRubyObject cond); @Deprecated(since = "10.0.0.0") public static RubyArray newBlankArray(Ruby runtime, int size) { return RubyArray.newBlankArray(runtime.getCurrentContext(), size); } /** * Construct the most efficient array shape for the given size. This should only be used when you * intend to populate all elements, since the packed arrays will be born with a nonzero size and * would have to be unpacked to partially populate. * * We nil-fill all cases, to ensure nulls will never leak out if there's an unpopulated element * or an index accessed before assignment. * * @param context the current thread context * @param size the size * @return a RubyArray shaped for the given size */ public static RubyArray newBlankArray(ThreadContext context, int size) { return RubyArrayNative.newBlankArray(context, size); } @JRubyMethod(name = "try_convert", meta = true) public static IRubyObject try_convert(ThreadContext context, IRubyObject self, IRubyObject arg) { return arg.checkArrayType(); } @JRubyMethod(name = "pack") public abstract RubyString pack(ThreadContext context, IRubyObject obj); @JRubyMethod(name = "pack") public abstract RubyString pack(ThreadContext context, IRubyObject obj, IRubyObject maybeOpts); @JRubyMethod(name = "dig") public abstract IRubyObject dig(ThreadContext context, IRubyObject arg0); @JRubyMethod(name = "dig") public abstract IRubyObject dig(ThreadContext context, IRubyObject arg0, IRubyObject arg1); @JRubyMethod(name = "dig") public abstract IRubyObject dig(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2); @JRubyMethod(name = "dig", required = 1, rest = true, checkArity = false) public abstract IRubyObject dig(ThreadContext context, IRubyObject[] args); @JRubyMethod(name = "max") public abstract IRubyObject max(ThreadContext context, Block block); @JRubyMethod(name = "max") public abstract IRubyObject max(ThreadContext context, IRubyObject num, Block block); @JRubyMethod(name = "min") public abstract IRubyObject min(ThreadContext context, Block block); @JRubyMethod(name = "min") public abstract IRubyObject min(ThreadContext context, IRubyObject num, Block block); @JRubyMethod public abstract IRubyObject minmax(ThreadContext context, Block block); @Override public Class getJavaClass() { return List.class; } @Deprecated(since = "10.0.0.0") public void copyInto(IRubyObject[] target, int start) { copyInto(getCurrentContext(), target, start); } /** * Copy the values contained in this array into the target array at the specified offset. * It is expected that the target array is large enough to hold all necessary values. */ public abstract void copyInto(ThreadContext context, IRubyObject[] target, int start); @Deprecated(since = "10.0.0.0") public void copyInto(IRubyObject[] target, int start, int len) { copyInto(getCurrentContext(), target, start, len); } /** * Copy the specified number of values contained in this array into the target array at the specified offset. * It is expected that the target array is large enough to hold all necessary values. */ public abstract void copyInto(ThreadContext context, IRubyObject[] target, int start, int len); @Override public abstract boolean equals(Object other); @JRubyMethod public abstract IRubyObject rfind(ThreadContext context, Block block); @JRubyMethod public abstract IRubyObject rfind(ThreadContext context, IRubyObject ifnone, Block block); @Deprecated(since = "10.0.0.0", forRemoval = true) @SuppressWarnings("removal") public static void marshalTo(RubyArray array, org.jruby.runtime.marshal.MarshalStream output) throws IOException { marshalTo(((RubyBasicObject) array).getCurrentContext(), array, output); } @Deprecated(since = "10.0.0.0", forRemoval = true) @SuppressWarnings("removal") public static void marshalTo(ThreadContext context, RubyArray array, org.jruby.runtime.marshal.MarshalStream output) throws IOException { output.registerLinkTarget(context, array); int length = array.getLength(); output.writeInt(length); for (int i = 0; i < length; i++) { output.dumpObject(array.eltOk(i)); } } public static void marshalTo(ThreadContext context, RubyOutputStream out, RubyArray array, MarshalDumper output) { output.registerObject(array); int length = array.getLength(); output.writeInt(out, length); for (int i = 0; i < length; i++) { output.dumpObject(context, out, array.eltOk(i)); } } @Deprecated(since = "10.0.0.0", forRemoval = true) @SuppressWarnings("removal") public static RubyArray unmarshalFrom(org.jruby.runtime.marshal.UnmarshalStream input) throws IOException { int size = input.unmarshalInt(); var context = input.getRuntime().getCurrentContext(); // FIXME: We used to use newArrayBlankInternal but this will not hash into a HashSet without an NPE. // we create this now with an empty, nulled array so it's available for links in the marshal data var result = (RubyArray) input.entry(Create.allocArray(context, size)); for (int i = 0; i < size; i++) { result.append(context, input.unmarshalObject()); } return result; } public static RubyArray unmarshalFrom(ThreadContext context, RubyInputStream in, MarshalLoader input) { int size = input.unmarshalInt(context, in); // FIXME: We used to use newArrayBlankInternal but this will not hash into a HashSet without an NPE. // we create this now with an empty, nulled array so it's available for links in the marshal data var result = (RubyArray) input.entry(Create.allocArray(context, size)); for (int i = 0; i < size; i++) { result.append(context, input.unmarshalObject(context, in)); } return result; } private static JavaSites.ArraySites sites(ThreadContext context) { return context.sites.Array; } /** * Returns a stream of each IRubyObject * @return */ public abstract Stream rubyStream(); }