// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-regexp-match-indices --expose-gc --stack-size=100

// Sanity test.
{
  const re = /a+(?<Z>z)?/;
  const m = re.exec("xaaaz");

  assertEquals(m.indices, [[1, 5], [4, 5]]);
  assertEquals(m.indices.groups, {'Z': [4, 5]})
}

// Capture groups that are not matched return `undefined`.
{
  const re = /a+(?<Z>z)?/;
  const m = re.exec("xaaay");

  assertEquals(m.indices, [[1, 4], undefined]);
  assertEquals(m.indices.groups, {'Z': undefined});
}

// Two capture groups.
{
  const re = /a+(?<A>zz)?(?<B>ii)?/;
  const m = re.exec("xaaazzii");

  assertEquals(m.indices, [[1, 8], [4, 6], [6, 8]]);
  assertEquals(m.indices.groups, {'A': [4, 6], 'B': [6, 8]});
}

// No capture groups.
{
  const re = /a+/;
  const m = re.exec("xaaazzii");

  assertEquals(m.indices [[1, 4]]);
  assertEquals(m.indices.groups, undefined);
}

// No match.
{
  const re = /a+/;
  const m = re.exec("xzzii");

  assertEquals(null, m);
}

// Unnamed capture groups.
{
  const re = /a+(z)?/;
  const m = re.exec("xaaaz")

  assertEquals(m.indices, [[1, 5], [4, 5]]);
  assertEquals(m.indices.groups, undefined)
}

// Named and unnamed capture groups.
{
  const re = /a+(z)?(?<Y>y)?/;
  const m = re.exec("xaaazyy")

  assertEquals(m.indices, [[1, 6], [4, 5], [5, 6]]);
  assertEquals(m.indices.groups, {'Y': [5, 6]})
}


// Verify property overwrite.
{
  const re = /a+(?<Z>z)?/;
  const m = re.exec("xaaaz");

  m.indices = null;
  assertEquals(null, m.indices);
}

// Mess with array prototype, we should still do the right thing.
{
  Object.defineProperty(Array.prototype, "groups", {
    get: () => {
      assertUnreachable();
      return null;
    },
    set: (x) => {
      assertUnreachable();
    }
  });

  Object.defineProperty(Array.prototype, "0", {
    get: () => {
      assertUnreachable();
      return null;
    },
    set: (x) => {
      assertUnreachable();
    }
  });

  const re = /a+(?<Z>z)?/;
  const m = re.exec("xaaaz");

  assertEquals(m.indices.groups, {'Z': [4, 5]})
}

// Test atomic regexp.
{
  const m = /undefined/.exec();

  assertEquals(m.indices, [[0, 9]]);
}

// Test deleting unrelated fields does not break.
{
  const m = /undefined/.exec();
  delete m['index'];
  gc();
  assertEquals(m.indices, [[0, 9]]);
}

// Stack overflow.
{
  const re = /a+(?<Z>z)?/;
  const m = re.exec("xaaaz");

  function rec() {
    try {
      return rec();
    } catch (e) {
      assertEquals(m.indices, [[1, 5], [4, 5]]);
      assertEquals(m.indices.groups, {'Z': [4, 5]})
      return true;
    }
  }
  assertTrue(rec());
}

// Match between matches.
{
  const re = /a+(?<A>zz)?(?<B>ii)?/;
  const m = re.exec("xaaazzii");
  assertTrue(/b+(?<C>cccc)?/.test("llllllbbbbbbcccc"));
  assertEquals(m.indices, [[1, 8], [4, 6], [6, 8]]);
  assertEquals(m.indices.groups, {'A': [4, 6], 'B': [6, 8]});
}
