///|
pub fn get_array_method(data : ArrayData, prop : String) -> Value {
  match prop {
    "push" => {
      let arr = data
      make_native_func("push", fn(args) {
        for arg in args {
          arr.elements.push(arg)
        }
        Number(arr.elements.length().to_double())
      })
    }
    "pop" => {
      let arr = data
      make_native_func("pop", fn(_args) {
        if arr.elements.length() == 0 {
          Undefined
        } else {
          arr.elements.pop() |> Option::unwrap
        }
      })
    }
    "shift" => {
      let arr = data
      make_native_func("shift", fn(_args) {
        if arr.elements.length() == 0 {
          return Undefined
        }
        let first = arr.elements[0]
        let new_elems : Array[Value] = []
        for i = 1; i < arr.elements.length(); i = i + 1 {
          new_elems.push(arr.elements[i])
        }
        while arr.elements.length() > 0 {
          let _ = arr.elements.pop()

        }
        for el in new_elems {
          arr.elements.push(el)
        }
        first
      })
    }
    "unshift" => {
      let arr = data
      make_native_func("unshift", fn(args) {
        let old : Array[Value] = []
        for el in arr.elements {
          old.push(el)
        }
        while arr.elements.length() > 0 {
          let _ = arr.elements.pop()

        }
        for arg in args {
          arr.elements.push(arg)
        }
        for el in old {
          arr.elements.push(el)
        }
        Number(arr.elements.length().to_double())
      })
    }
    "join" => {
      let arr = data
      make_native_func("join", fn(args) {
        let sep = if args.length() > 0 {
          match args[0] {
            Undefined => ","
            v => v.to_string()
          }
        } else {
          ","
        }
        let parts : Array[String] = arr.elements.map(fn(v) {
          match v {
            Null | Undefined => ""
            _ => v.to_string()
          }
        })
        String_(parts.join(sep))
      })
    }
    "indexOf" => {
      let arr = data
      make_native_func("indexOf", fn(args) {
        let search = if args.length() > 0 { args[0] } else { Undefined }
        let len = arr.elements.length()
        let from_idx = if args.length() > 1 {
          to_number(args[1]).to_int()
        } else {
          0
        }
        let start = if from_idx < 0 {
          let s = len + from_idx
          if s < 0 {
            0
          } else {
            s
          }
        } else {
          from_idx
        }
        // If start >= length, return -1
        if start >= len {
          return Number(-1.0)
        }
        for i = start; i < len; i = i + 1 {
          if strict_equal_val(arr.elements[i], search) {
            return Number(i.to_double())
          }
        }
        Number(-1.0)
      })
    }
    "lastIndexOf" => {
      let arr = data
      make_native_func("lastIndexOf", fn(args) {
        let search = if args.length() > 0 { args[0] } else { Undefined }
        let start = if args.length() > 1 {
          to_number(args[1]).to_int()
        } else {
          arr.elements.length() - 1
        }
        let from = if start < 0 {
          arr.elements.length() + start
        } else if start >= arr.elements.length() {
          arr.elements.length() - 1
        } else {
          start
        }
        for i = from; i >= 0; i = i - 1 {
          if strict_equal_val(arr.elements[i], search) {
            return Number(i.to_double())
          }
        }
        Number(-1.0)
      })
    }
    "includes" => {
      let arr = data
      make_native_func("includes", fn(args) {
        let search = if args.length() > 0 { args[0] } else { Undefined }
        let from_idx = if args.length() > 1 {
          to_number(args[1]).to_int()
        } else {
          0
        }
        let len = arr.elements.length()
        let start = if from_idx < 0 {
          let s = len + from_idx
          if s < 0 {
            0
          } else {
            s
          }
        } else {
          from_idx
        }
        for i = start; i < len; i = i + 1 {
          if same_value_zero(arr.elements[i], search) {
            return Bool(true)
          }
        }
        Bool(false)
      })
    }
    "slice" => {
      let arr = data
      make_native_func("slice", fn(args) {
        let alen = arr.elements.length()
        let mut start = if args.length() > 0 {
          to_number(args[0]).to_int()
        } else {
          0
        }
        let mut end = if args.length() > 1 {
          match args[1] {
            Undefined => alen
            _ => to_number(args[1]).to_int()
          }
        } else {
          alen
        }
        if start < 0 {
          start = alen + start
          if start < 0 {
            start = 0
          }
        }
        if end < 0 {
          end = alen + end
          if end < 0 {
            end = 0
          }
        }
        if end > alen {
          end = alen
        }
        let result : Array[Value] = []
        for i = start; i < end; i = i + 1 {
          result.push(arr.elements[i])
        }
        Array({ elements: result })
      })
    }
    "concat" => {
      let arr = data
      make_native_func("concat", fn(args) {
        let result : Array[Value] = []
        for el in arr.elements {
          result.push(el)
        }
        for arg in args {
          match arg {
            Array(other) =>
              for el in other.elements {
                result.push(el)
              }
            _ => result.push(arg)
          }
        }
        Array({ elements: result })
      })
    }
    "reverse" => {
      let arr = data
      make_native_func("reverse", fn(_args) {
        arr.elements.rev_in_place()
        Array(arr)
      })
    }
    "fill" => {
      let arr = data
      make_native_func("fill", fn(args) {
        let fill_val = if args.length() > 0 { args[0] } else { Undefined }
        let alen = arr.elements.length()
        let mut start = if args.length() > 1 {
          to_number(args[1]).to_int()
        } else {
          0
        }
        let mut end = if args.length() > 2 {
          match args[2] {
            Undefined => alen
            _ => to_number(args[2]).to_int()
          }
        } else {
          alen
        }
        // Normalize negative start
        if start < 0 {
          start = alen + start
          if start < 0 {
            start = 0
          }
        }
        // Clamp start to array length
        if start > alen {
          start = alen
        }
        // Normalize negative end
        if end < 0 {
          end = alen + end
          if end < 0 {
            end = 0
          }
        }
        // Clamp end to array length
        if end > alen {
          end = alen
        }
        for i = start; i < end; i = i + 1 {
          arr.elements[i] = fill_val
        }
        Array(arr)
      })
    }
    "toString" => {
      let arr = data
      make_native_func("toString", fn(_args) {
        let parts : Array[String] = arr.elements.map(fn(v) {
          match v {
            Null | Undefined => ""
            _ => v.to_string()
          }
        })
        String_(parts.join(","))
      })
    }
    "at" => {
      let arr = data
      make_native_func("at", fn(args) {
        // Array.prototype.at() - access element at index (supports negative)
        let idx = if args.length() > 0 {
          to_number(args[0]).to_int()
        } else {
          0
        }
        let len = arr.elements.length()
        let actual_idx = if idx < 0 { len + idx } else { idx }
        if actual_idx >= 0 && actual_idx < len {
          arr.elements[actual_idx]
        } else {
          Undefined
        }
      })
    }
    "copyWithin" => {
      let arr = data
      make_native_func("copyWithin", fn(args) {
        // Array.prototype.copyWithin(target, start, end?)
        let len = arr.elements.length()
        let mut target = if args.length() > 0 {
          to_number(args[0]).to_int()
        } else {
          0
        }
        let mut start = if args.length() > 1 {
          to_number(args[1]).to_int()
        } else {
          0
        }
        let mut end = if args.length() > 2 {
          match args[2] {
            Undefined => len
            _ => to_number(args[2]).to_int()
          }
        } else {
          len
        }
        // Normalize negative indices
        if target < 0 {
          target = len + target
          if target < 0 {
            target = 0
          }
        }
        if start < 0 {
          start = len + start
          if start < 0 {
            start = 0
          }
        }
        if end < 0 {
          end = len + end
          if end < 0 {
            end = 0
          }
        }
        if end > len {
          end = len
        }
        // Copy elements
        let count = if end - start < len - target {
          end - start
        } else {
          len - target
        }
        if count > 0 {
          // Copy to temporary first to handle overlapping
          let temp : Array[Value] = []
          for i = start; i < start + count; i = i + 1 {
            temp.push(arr.elements[i])
          }
          for i = 0; i < count; i = i + 1 {
            if target + i < len {
              arr.elements[target + i] = temp[i]
            }
          }
        }
        Array(arr)
      })
    }
    "entries" => {
      let arr = data
      make_native_func("entries", fn(_args) {
        // Returns array of [index, value] pairs (basic non-iterator version)
        let result : Array[Value] = []
        for i = 0; i < arr.elements.length(); i = i + 1 {
          result.push(
            Array({ elements: [Number(i.to_double()), arr.elements[i]] }),
          )
        }
        Array({ elements: result })
      })
    }
    "keys" => {
      let arr = data
      make_native_func("keys", fn(_args) {
        // Returns array of keys (basic non-iterator version)
        let result : Array[Value] = []
        for i = 0; i < arr.elements.length(); i = i + 1 {
          result.push(Number(i.to_double()))
        }
        Array({ elements: result })
      })
    }
    "values" => {
      let arr = data
      make_native_func("values", fn(_args) {
        // Returns array of values (basic non-iterator version)
        // Return a copy to prevent mutation of the original
        let result : Array[Value] = []
        for el in arr.elements {
          result.push(el)
        }
        Array({ elements: result })
      })
    }
    "splice" => {
      let arr = data
      make_native_func("splice", fn(args) {
        let alen = arr.elements.length()
        let mut start = if args.length() > 0 {
          to_number(args[0]).to_int()
        } else {
          0
        }
        if start < 0 {
          start = alen + start
          if start < 0 {
            start = 0
          }
        }
        if start > alen {
          start = alen
        }
        let del_count = if args.length() > 1 {
          let d_num = to_number(args[1])
          // Handle Infinity and negative values
          if d_num.is_inf() || d_num >= (alen - start).to_double() {
            alen - start
          } else {
            let d = d_num.to_int()
            if d < 0 {
              0
            } else {
              d
            }
          }
        } else {
          alen - start
        }
        let removed : Array[Value] = []
        for i = start; i < start + del_count; i = i + 1 {
          removed.push(arr.elements[i])
        }
        let new_items : Array[Value] = []
        for i = 2; i < args.length(); i = i + 1 {
          new_items.push(args[i])
        }
        let tail : Array[Value] = []
        for i = start + del_count; i < alen; i = i + 1 {
          tail.push(arr.elements[i])
        }
        while arr.elements.length() > start {
          let _ = arr.elements.pop()

        }
        for item in new_items {
          arr.elements.push(item)
        }
        for item in tail {
          arr.elements.push(item)
        }
        Array({ elements: removed })
      })
    }
    "sort" => {
      let arr = data
      make_native_func("sort", fn(_args) {
        // Default sort: convert to strings and compare
        arr.elements.sort_by(fn(a, b) {
          let sa = a.to_string()
          let sb = b.to_string()
          if sa < sb {
            -1
          } else if sa > sb {
            1
          } else {
            0
          }
        })
        Array(arr)
      })
    }
    _ => Undefined
  }
}

///|
pub fn get_array_method_with_interp(data : ArrayData, prop : String) -> Value {
  match prop {
    "forEach" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("forEach", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            for i = 0; i < arr.elements.length(); i = i + 1 {
              let _ = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )

            }
            Undefined
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "map" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("map", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            let result : Array[Value] = []
            for i = 0; i < arr.elements.length(); i = i + 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              result.push(val)
            }
            Array({ elements: result })
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "filter" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("filter", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            let result : Array[Value] = []
            for i = 0; i < arr.elements.length(); i = i + 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              if is_truthy(val) {
                result.push(arr.elements[i])
              }
            }
            Array({ elements: result })
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "reduce" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("reduce", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let loc = @token.Loc::default()
            let mut acc : Value = Undefined
            let mut start = 0
            if args.length() > 1 {
              acc = args[1]
            } else {
              if arr.elements.length() == 0 {
                raise @errors.TypeError(
                  message="Reduce of empty array with no initial value",
                )
              }
              acc = arr.elements[0]
              start = 1
            }
            for i = start; i < arr.elements.length(); i = i + 1 {
              acc = interp.call_value(
                callback,
                Undefined,
                [acc, arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
            }
            acc
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "reduceRight" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("reduceRight", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let loc = @token.Loc::default()
            let mut acc : Value = Undefined
            let mut start = arr.elements.length() - 1
            if args.length() > 1 {
              acc = args[1]
            } else {
              if arr.elements.length() == 0 {
                raise @errors.TypeError(
                  message="Reduce of empty array with no initial value",
                )
              }
              acc = arr.elements[start]
              start = start - 1
            }
            for i = start; i >= 0; i = i - 1 {
              acc = interp.call_value(
                callback,
                Undefined,
                [acc, arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
            }
            acc
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "find" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("find", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            for i = 0; i < arr.elements.length(); i = i + 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              if is_truthy(val) {
                return arr.elements[i]
              }
            }
            Undefined
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "findIndex" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("findIndex", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            for i = 0; i < arr.elements.length(); i = i + 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              if is_truthy(val) {
                return Number(i.to_double())
              }
            }
            Number(-1.0)
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "every" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("every", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            for i = 0; i < arr.elements.length(); i = i + 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              if not(is_truthy(val)) {
                return Bool(false)
              }
            }
            Bool(true)
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "some" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("some", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            for i = 0; i < arr.elements.length(); i = i + 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              if is_truthy(val) {
                return Bool(true)
              }
            }
            Bool(false)
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "findLast" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("findLast", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            for i = arr.elements.length() - 1; i >= 0; i = i - 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              if is_truthy(val) {
                return arr.elements[i]
              }
            }
            Undefined
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "findLastIndex" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("findLastIndex", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            for i = arr.elements.length() - 1; i >= 0; i = i - 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              if is_truthy(val) {
                return Number(i.to_double())
              }
            }
            Number(-1.0)
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "toReversed" => {
      let arr = data
      make_native_func("toReversed", fn(_args) {
        // Non-mutating reverse
        let result : Array[Value] = []
        for i = arr.elements.length() - 1; i >= 0; i = i - 1 {
          result.push(arr.elements[i])
        }
        Array({ elements: result })
      })
    }
    "toSorted" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("toSorted", fn(interp, this_val, args) {
            let _ = this_val
            // Create a copy of the array
            let result : Array[Value] = []
            for el in arr.elements {
              result.push(el)
            }
            // Sort the copy
            if args.length() > 0 {
              match args[0] {
                Undefined =>
                  // Default sort
                  result.sort_by(fn(a, b) {
                    let sa = a.to_string()
                    let sb = b.to_string()
                    if sa < sb {
                      -1
                    } else if sa > sb {
                      1
                    } else {
                      0
                    }
                  })
                _ => {
                  let comparefn = args[0]
                  let loc = @token.Loc::default()
                  // Store any exception to re-throw after sort (spec requires propagation)
                  let mut caught_error : Error? = None
                  result.sort_by(fn(a, b) {
                    // Skip comparisons if we already have an error
                    if caught_error is Some(_) {
                      return 0
                    }
                    let compare_result = interp.call_value(
                      comparefn,
                      Undefined,
                      [a, b],
                      loc,
                    ) catch {
                      e => {
                        caught_error = Some(e)
                        return 0
                      }
                    }
                    // Use sign-based comparison to avoid truncating fractional values
                    let cmp = to_number(compare_result) catch {
                      e => {
                        caught_error = Some(e)
                        return 0
                      }
                    }
                    if cmp.is_nan() {
                      0
                    } else if cmp < 0.0 {
                      -1
                    } else if cmp > 0.0 {
                      1
                    } else {
                      0
                    }
                  })
                  // Re-throw any caught exception per spec
                  match caught_error {
                    Some(e) => raise e
                    None => ()
                  }
                }
              }
            } else {
              result.sort_by(fn(a, b) {
                let sa = a.to_string()
                let sb = b.to_string()
                if sa < sb {
                  -1
                } else if sa > sb {
                  1
                } else {
                  0
                }
              })
            }
            Array({ elements: result })
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "toSpliced" => {
      let arr = data
      make_native_func("toSpliced", fn(args) {
        // Non-mutating splice
        let len = arr.elements.length()
        let mut start = if args.length() > 0 {
          to_number(args[0]).to_int()
        } else {
          0
        }
        if start < 0 {
          start = len + start
          if start < 0 {
            start = 0
          }
        }
        if start > len {
          start = len
        }
        let del_count = if args.length() > 1 {
          let d = to_number(args[1]).to_int()
          if d < 0 {
            0
          } else if d > len - start {
            len - start
          } else {
            d
          }
        } else {
          len - start
        }
        let result : Array[Value] = []
        // Copy before start
        for i = 0; i < start; i = i + 1 {
          result.push(arr.elements[i])
        }
        // Add new items
        for i = 2; i < args.length(); i = i + 1 {
          result.push(args[i])
        }
        // Copy after deleted portion
        for i = start + del_count; i < len; i = i + 1 {
          result.push(arr.elements[i])
        }
        Array({ elements: result })
      })
    }
    "with" => {
      let arr = data
      make_native_func("with", fn(args) {
        // Non-mutating element replacement
        let idx = if args.length() > 0 {
          to_number(args[0]).to_int()
        } else {
          0
        }
        let value = if args.length() > 1 { args[1] } else { Undefined }
        let len = arr.elements.length()
        let actual_idx = if idx < 0 { len + idx } else { idx }
        if actual_idx < 0 || actual_idx >= len {
          raise @errors.RangeError(message="Invalid index")
        }
        let result : Array[Value] = []
        for i = 0; i < len; i = i + 1 {
          if i == actual_idx {
            result.push(value)
          } else {
            result.push(arr.elements[i])
          }
        }
        Array({ elements: result })
      })
    }
    "flat" => {
      let arr = data
      make_native_func("flat", fn(args) {
        let depth = if args.length() > 0 {
          to_number(args[0]).to_int()
        } else {
          1
        }
        let result : Array[Value] = []
        flatten_array(arr.elements, depth, result)
        Array({ elements: result })
      })
    }
    "flatMap" => {
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("flatMap", fn(interp, this_val, args) {
            let _ = this_val
            let callback = if args.length() > 0 { args[0] } else { Undefined }
            let this_arg = if args.length() > 1 { args[1] } else { Undefined }
            let loc = @token.Loc::default()
            let result : Array[Value] = []
            for i = 0; i < arr.elements.length(); i = i + 1 {
              let val = interp.call_value(
                callback,
                this_arg,
                [arr.elements[i], Number(i.to_double()), Array(arr)],
                loc,
              )
              match val {
                Array(inner) =>
                  for el in inner.elements {
                    result.push(el)
                  }
                _ => result.push(val)
              }
            }
            Array({ elements: result })
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    "sort" => {
      // Sort with optional compareFn needs InterpreterCallable
      let arr = data
      Object({
        properties: {},
        symbol_properties: {},
        prototype: Null,
        callable: Some(
          InterpreterCallable("sort", fn(interp, this_val, args) {
            let _ = this_val
            if args.length() > 0 {
              match args[0] {
                Undefined =>
                  // Default sort
                  arr.elements.sort_by(fn(a, b) {
                    let sa = a.to_string()
                    let sb = b.to_string()
                    if sa < sb {
                      -1
                    } else if sa > sb {
                      1
                    } else {
                      0
                    }
                  })
                _ => {
                  let comparefn = args[0]
                  let loc = @token.Loc::default()
                  // Store any exception to re-throw after sort (spec requires propagation)
                  let mut caught_error : Error? = None
                  arr.elements.sort_by(fn(a, b) {
                    // Skip comparisons if we already have an error
                    if caught_error is Some(_) {
                      return 0
                    }
                    let result = interp.call_value(
                      comparefn,
                      Undefined,
                      [a, b],
                      loc,
                    ) catch {
                      e => {
                        caught_error = Some(e)
                        return 0
                      }
                    }
                    // Use sign-based comparison to avoid truncating fractional values
                    let cmp = to_number(result) catch {
                      e => {
                        caught_error = Some(e)
                        return 0
                      }
                    }
                    if cmp.is_nan() {
                      0
                    } else if cmp < 0.0 {
                      -1
                    } else if cmp > 0.0 {
                      1
                    } else {
                      0
                    }
                  })
                  // Re-throw any caught exception per spec
                  match caught_error {
                    Some(e) => raise e
                    None => ()
                  }
                }
              }
            } else {
              arr.elements.sort_by(fn(a, b) {
                let sa = a.to_string()
                let sb = b.to_string()
                if sa < sb {
                  -1
                } else if sa > sb {
                  1
                } else {
                  0
                }
              })
            }
            Array(arr)
          }),
        ),
        class_name: "Function",
        descriptors: {},
        symbol_descriptors: {},
        extensible: true,
      })
    }
    _ => get_array_method(data, prop)
  }
}

///|
fn flatten_array(
  elements : Array[Value],
  depth : Int,
  result : Array[Value],
) -> Unit {
  for el in elements {
    match el {
      Array(inner) =>
        if depth > 0 {
          flatten_array(inner.elements, depth - 1, result)
        } else {
          result.push(el)
        }
      _ => result.push(el)
    }
  }
}

///|
pub fn strict_equal_val(a : Value, b : Value) -> Bool {
  match (a, b) {
    (Number(a), Number(b)) => a == b
    (String_(a), String_(b)) => a == b
    (Bool(a), Bool(b)) => a == b
    (Null, Null) => true
    (Undefined, Undefined) => true
    (Object(a), Object(b)) => physical_equal(a, b)
    (Array(a), Array(b)) => physical_equal(a, b)
    (Symbol(a), Symbol(b)) => a.id == b.id // Symbols compare by identity
    (Promise(a), Promise(b)) => physical_equal(a, b) // Promises compare by reference
    _ => false
  }
}

///|
pub fn same_value_zero(a : Value, b : Value) -> Bool {
  match (a, b) {
    (Number(a), Number(b)) =>
      if a.is_nan() && b.is_nan() {
        true
      } else {
        a == b
      }
    _ => strict_equal_val(a, b)
  }
}

///|
pub fn same_value(a : Value, b : Value) -> Bool {
  // SameValue algorithm per ECMAScript spec
  // Differs from === in: NaN === NaN, and +0 !== -0
  match (a, b) {
    (Number(x), Number(y)) =>
      if x.is_nan() && y.is_nan() {
        true
      } else if x == 0.0 && y == 0.0 {
        // Check for +0 vs -0
        1.0 / x == 1.0 / y
      } else {
        x == y
      }
    _ => strict_equal_val(a, b)
  }
}