#!/bin/bash
#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Stop if something fails.
set -e

# Write out the source file.

mkdir src
cat >src/Main.java <<EOF
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

EOF

for i in {0..8192}; do echo "class Level1Class$i { }" >>src/Main.java; done
for i in {0..1024}; do echo "class Level2Class$i extends Level1Class0 { }" >>src/Main.java; done

cat >>src/Main.java <<EOF
class Level3Class0 extends Level2Class0 { }
class Level4Class0 extends Level3Class0 { }
class Level5Class0 extends Level4Class0 { }
class Level6Class0 extends Level5Class0 { }
class Level7Class0 extends Level6Class0 { }
class Level8Class0 extends Level7Class0 { }
class Level9Class0 extends Level8Class0 { }

public class Main {
  public static void main(String[] args) throws Exception {
    // 8193 classes at level 1 make sure we shall have an overflow if there are 13 or
    // less bits for the level 1 character. 1025 classes at level 2 similarly guarantees
    // an overflow if the number of bits for level 2 character is 10 or less. To test
    // type checks also for the depth overflow, we provide a hierarchy 9 levels deep.

    // Make sure the bitstrings are initialized.
    for (int i = 0; i <= 8192; ++i) {
      Class.forName("Level1Class" + i).newInstance();
    }
    for (int i = 0; i <= 1024; ++i) {
      Class.forName("Level2Class" + i).newInstance();
    }

    // Note: Using a different class for tests so that verification of Main.main() does
    // not try to resolve classes used by the tests. This guarantees uninitialized type
    // check bitstrings when we enter Main.main() and start initializing them above.
    Helper.testInstanceOf();
    Helper.testCheckCast();
  }
}

class Helper {
  public static void testInstanceOf() throws Exception {
    for (int i = 1; i <= 9; ++i) {
      Object o = createInstance("Level" + i + "Class0");
      assertTrue(o instanceof Level1Class0);
      if (o instanceof Level2Class0) {
        assertFalse(i < 2);
      } else {
        assertTrue(i < 2);
      }
      if (o instanceof Level3Class0) {
        assertFalse(i < 3);
      } else {
        assertTrue(i < 3);
      }
      if (o instanceof Level4Class0) {
        assertFalse(i < 4);
      } else {
        assertTrue(i < 4);
      }
      if (o instanceof Level5Class0) {
        assertFalse(i < 5);
      } else {
        assertTrue(i < 5);
      }
      if (o instanceof Level6Class0) {
        assertFalse(i < 6);
      } else {
        assertTrue(i < 6);
      }
      if (o instanceof Level7Class0) {
        assertFalse(i < 7);
      } else {
        assertTrue(i < 7);
      }
      if (o instanceof Level8Class0) {
        assertFalse(i < 8);
      } else {
        assertTrue(i < 8);
      }
      if (o instanceof Level9Class0) {
        assertFalse(i < 9);
      } else {
        assertTrue(i < 9);
      }
    }

    assertTrue(createInstance("Level1Class8192") instanceof Level1Class8192);
    assertFalse(createInstance("Level1Class8192") instanceof Level1Class0);
    assertTrue(createInstance("Level2Class1024") instanceof Level2Class1024);
    assertTrue(createInstance("Level2Class1024") instanceof Level1Class0);
    assertFalse(createInstance("Level2Class1024") instanceof Level2Class0);
  }

  public static void testCheckCast() throws Exception {
    for (int i = 1; i <= 9; ++i) {
      Object o = createInstance("Level" + i + "Class0");
      Level1Class0 l1c0 = (Level1Class0) o;
      try {
        Level2Class0 l2c0 = (Level2Class0) o;
        assertFalse(i < 2);
      } catch (ClassCastException cce) {
        assertTrue(i < 2);
      }
      try {
        Level3Class0 l3c0 = (Level3Class0) o;
        assertFalse(i < 3);
      } catch (ClassCastException cce) {
        assertTrue(i < 3);
      }
      try {
        Level4Class0 l4c0 = (Level4Class0) o;
        assertFalse(i < 4);
      } catch (ClassCastException cce) {
        assertTrue(i < 4);
      }
      try {
        Level5Class0 l5c0 = (Level5Class0) o;
        assertFalse(i < 5);
      } catch (ClassCastException cce) {
        assertTrue(i < 5);
      }
      try {
        Level6Class0 l6c0 = (Level6Class0) o;
        assertFalse(i < 6);
      } catch (ClassCastException cce) {
        assertTrue(i < 6);
      }
      try {
        Level7Class0 l7c0 = (Level7Class0) o;
        assertFalse(i < 7);
      } catch (ClassCastException cce) {
        assertTrue(i < 7);
      }
      try {
        Level8Class0 l8c0 = (Level8Class0) o;
        assertFalse(i < 8);
      } catch (ClassCastException cce) {
        assertTrue(i < 8);
      }
      try {
        Level9Class0 l9c0 = (Level9Class0) o;
        assertFalse(i < 9);
      } catch (ClassCastException cce) {
        assertTrue(i < 9);
      }
    }

    Level1Class8192 l1c8192 = (Level1Class8192) createInstance("Level1Class8192");
    try {
      Level1Class0 l1c0 = (Level1Class0) createInstance("Level1Class8192");
      throw new AssertionError("Unexpected");
    } catch (ClassCastException expected) {}
    Level2Class1024 l2c1024 = (Level2Class1024) createInstance("Level2Class1024");
    Level1Class0 l1c0 = (Level1Class0) createInstance("Level2Class1024");
    try {
      Level2Class0 l2c0 = (Level2Class0) createInstance("Level2Class1024");
      throw new AssertionError("Unexpected");
    } catch (ClassCastException expected) {}
  }

  public static Object createInstance(String className) throws Exception {
    return Class.forName(className).newInstance();
  }

  public static void assertTrue(boolean value) throws Exception {
    if (!value) {
      throw new AssertionError();
    }
  }

  public static void assertFalse(boolean value) throws Exception {
    if (value) {
      throw new AssertionError();
    }
  }
}
EOF

./default-build "$@"
