Package org.codehaus.groovy.runtime

# Class MultipleAssignmentSupport

java.lang.Object
org.codehaus.groovy.runtime.MultipleAssignmentSupport

@GroovyABI(since="6.0.0") public final class MultipleAssignmentSupport extends Object
Runtime helpers used by the GEP-20 multi-assignment destructuring bytecode.

These are called from generated bytecode only. User code should not rely on them.

Since:
  6.0.0

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static Object`
  `nonTailRestSlice(Object rhs, int fromIndex, int toIndex)`
  Resolve the value for a head or middle rest binding.
  `static Object`
  `tailRest(Object rhs, int fromIndex, Iterator<?> seq)`
  Resolve the value for a tail rest binding (`*t`) at declarator index `fromIndex`.

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Method Details

  - ### tailRest

    public static Object tailRest(Object rhs, int fromIndex, Iterator<?> seq)
    Resolve the value for a tail rest binding (`*t`) at declarator index `fromIndex`. The GEP-20 dispatch model has three paths:
    - Path A — `Stream<T>`: wrap the already-advanced iterator in a fresh sequential Stream and chain `onClose` to the original. The new Stream reports no spliterator characteristics (preserving them would require reading the original spliterator, which is mutually exclusive with the iterator path used for head extraction).
    - Path B — receiver with a resolvable `getAt(IntRange)`. Implemented as fast-path `instanceof` branches for `List`, `CharSequence`, and Java arrays (size-aware so out-of-bounds `fromIndex` returns the canonical empty value without calling user code), with an MOP-dispatched fallback for any other Path B receiver — `BitSet` (returns `BitSet`), user custom classes, etc.
    - Path C — anything else iterable: return the already-advanced iterator.

    The actual `instanceof` checks are ordered for performance (List/CharSequence/array fast paths first, then Stream, then MOP fallback, then iterator) but the conceptual dispatch order is A, then B, then C.

    Primitive streams (`IntStream`, `LongStream`, `DoubleStream`) are not subtypes of `Stream<T>` and therefore fall through to Path B (if they expose `getAt(IntRange)`) or Path C; their elements are boxed by the underlying iterator.

    Parameters:
      `rhs` - the original RHS value
      `fromIndex` - the declarator position of the rest binding
      `seq` - the iterator that has already yielded the preceding fixed-slot values

  - ### nonTailRestSlice

    public static Object nonTailRestSlice(Object rhs, int fromIndex, int toIndex)
    Resolve the value for a head or middle rest binding. Requires a sized, indexable RHS (List, CharSequence, or array — or any other receiver with a non-reversing `getAt(IntRange)`); for other RHS types this routes through the MOP so that:
    - Truly non-indexable sources (iterators, sets, custom Iterables without a `getAt(IntRange)` extension) fail fast with `MissingMethodException` — preventing silent materialisation of unbounded sources.
    - `Stream` RHS resolves `StreamGroovyMethods.getAt(Stream, IntRange)`, which rejects the destructuring slice's reverse range with `IllegalArgumentException("reverse range")` — also fail-fast, also no materialisation. (Head/middle rest from `Stream` is rejected at compile time under `@CompileStatic`; the IAE only surfaces in dynamic mode.)

    When the RHS is shorter than the sum of fixed slots around the rest, the computed range is inverted. DGM's `getAt(IntRange)` would reverse an inverted range; for destructuring we want an empty slice instead, so this helper short-circuits that case for the sized fast paths (List/CharSequence/array). Other indexable receivers are responsible for their own bounds handling.

    Parameters:
      `rhs` - the original RHS value
      `fromIndex` - positive index where the slice begins
      `toIndex` - negative index where the slice ends (inclusive)