Class MultipleAssignmentSupport

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

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 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 AStream<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)