content: /*
   * Copyright (C) 2016 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.
   */
  
  public class Main {
    public static void main(String[] args) {
      System.loadLibrary(args[0]);
      Thread testThread = new Thread() {
        public void run() {
          performTest();
        }
      };
      testThread.start();
      try {
        testThread.join(20 * 1000);  // 20s timeout.
      } catch (InterruptedException ie) {
        System.out.println("Interrupted.");
        System.exit(1);
      }
      Thread.State state = testThread.getState();
      if (state != Thread.State.TERMINATED) {
        System.out.println("Test timed out, current state: " + state);
        System.exit(1);
      }
    }
  
    public static void performTest() {
      new SubMain();
      if ($noinline$returnInt() != 53) {
        throw new Error("Unexpected return value");
      }
      if ($noinline$returnFloat() != 42.2f) {
        throw new Error("Unexpected return value");
      }
      if ($noinline$returnDouble() != Double.longBitsToDouble(0xF000000000001111L)) {
        throw new Error("Unexpected return value ");
      }
      if ($noinline$returnLong() != 0xFFFF000000001111L) {
        throw new Error("Unexpected return value");
      }
  
      try {
        $noinline$deopt();
      } catch (Exception e) {}
      DeoptimizationController.stopDeoptimization();
  
      $noinline$inlineCache(new Main(), /* isSecondInvocation */ false);
      if ($noinline$inlineCache(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) {
        throw new Error("Unexpected return value");
      }
  
      $noinline$inlineCache2(new Main(), /* isSecondInvocation */ false);
      if ($noinline$inlineCache2(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) {
        throw new Error("Unexpected return value");
      }
  
      // Test polymorphic inline cache to the same target (inlineCache3).
      $noinline$inlineCache3(new Main(), /* isSecondInvocation */ false);
      $noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ false);
      if ($noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ true) != null) {
        throw new Error("Unexpected return value");
      }
  
      $noinline$stackOverflow(new Main(), /* isSecondInvocation */ false);
      $noinline$stackOverflow(new SubMain(), /* isSecondInvocation */ true);
  
      $opt$noinline$testOsrInlineLoop(null);
      System.out.println("b28210356 passed.");
    }
  
    public static int $noinline$returnInt() {
      if (doThrow) throw new Error("");
      int i = 0;
      for (; i < 100000; ++i) {
      }
      while (!isInOsrCode("$noinline$returnInt")) {}
      System.out.println(i);
      return 53;
    }
  
    public static float $noinline$returnFloat() {
      if (doThrow) throw new Error("");
      int i = 0;
      for (; i < 200000; ++i) {
      }
      while (!isInOsrCode("$noinline$returnFloat")) {}
      System.out.println(i);
      return 42.2f;
    }
  
    public static double $noinline$returnDouble() {
      if (doThrow) throw new Error("");
      int i = 0;
      for (; i < 300000; ++i) {
      }
      while (!isInOsrCode("$noinline$returnDouble")) {}
      System.out.println(i);
      return Double.longBitsToDouble(0xF000000000001111L);
    }
  
    public static long $noinline$returnLong() {
      if (doThrow) throw new Error("");
      int i = 0;
      for (; i < 400000; ++i) {
      }
      while (!isInOsrCode("$noinline$returnLong")) {}
      System.out.println(i);
      return 0xFFFF000000001111L;
    }
  
    public static void $noinline$deopt() {
      if (doThrow) throw new Error("");
      int i = 0;
      for (; i < 100000; ++i) {
      }
      while (!isInOsrCode("$noinline$deopt")) {}
      DeoptimizationController.startDeoptimization();
    }
  
    public static Class $noinline$inlineCache(Main m, boolean isSecondInvocation) {
      // If we are running in non-JIT mode, or were unlucky enough to get this method
      // already JITted, just return the expected value.
      if (!isInInterpreter("$noinline$inlineCache")) {
        return SubMain.class;
      }
  
      ensureHasProfilingInfo("$noinline$inlineCache");
  
      // Ensure that we have OSR code to jump to.
      if (isSecondInvocation) {
        ensureHasOsrCode("$noinline$inlineCache");
      }
  
      // This call will be optimized in the OSR compiled code
      // to check and deoptimize if m is not of type 'Main'.
      Main other = m.inlineCache();
  
      // Jump to OSR compiled code. The second run
      // of this method will have 'm' as a SubMain, and the compiled
      // code we are jumping to will have wrongly optimize other as being a
      // 'Main'.
      if (isSecondInvocation) {
        while (!isInOsrCode("$noinline$inlineCache")) {}
      }
  
      // We used to wrongly optimize this call and assume 'other' was a 'Main'.
      return other.returnClass();
    }
  
    public static Class $noinline$inlineCache2(Main m, boolean isSecondInvocation) {
      // If we are running in non-JIT mode, or were unlucky enough to get this method
      // already JITted, just return the expected value.
      if (!isInInterpreter("$noinline$inlineCache2")) {
        return SubMain.class;
      }
  
      ensureHasProfilingInfo("$noinline$inlineCache2");
  
      // Ensure that we have OSR code to jump to.
      if (isSecondInvocation) {
        ensureHasOsrCode("$noinline$inlineCache2");
      }
  
      // This call will be optimized in the OSR compiled code
      // to check and deoptimize if m is not of type 'Main'.
      Main other = m.inlineCache2();
  
      // Jump to OSR compiled code. The second run
      // of this method will have 'm' as a SubMain, and the compiled
      // code we are jumping to will have wrongly optimize other as being null.
      if (isSecondInvocation) {
        while (!isInOsrCode("$noinline$inlineCache2")) {}
      }
  
      // We used to wrongly optimize this code and assume 'other' was always null.
      return (other == null) ? null : other.returnClass();
    }
  
    public static Class $noinline$inlineCache3(Main m, boolean isSecondInvocation) {
      // If we are running in non-JIT mode, or were unlucky enough to get this method
      // already JITted, just return the expected value.
      if (!isInInterpreter("$noinline$inlineCache3")) {
        return null;
      }
  
      ensureHasProfilingInfo("$noinline$inlineCache3");
  
      // Ensure that we have OSR code to jump to.
      if (isSecondInvocation) {
        ensureHasOsrCode("$noinline$inlineCache3");
      }
  
      // This call will be optimized in the OSR compiled code
      // to check and deoptimize if m is not of type 'Main'.
      Main other = m.inlineCache3();
  
      // Jump to OSR compiled code. The second run
      // of this method will have 'm' as a SubMain, and the compiled
      // code we are jumping to will have wrongly optimize other as being null.
      if (isSecondInvocation) {
        while (!isInOsrCode("$noinline$inlineCache3")) {}
      }
  
      // We used to wrongly optimize this code and assume 'other' was always null.
      return (other == null) ? null : other.returnClass();
    }
  
    public Main inlineCache() {
      return new Main();
    }
  
    public Main inlineCache2() {
      return null;
    }
  
    public Main inlineCache3() {
      return null;
    }
  
    public Class returnClass() {
      return Main.class;
    }
  
    public void otherInlineCache() {
      return;
    }
  
    public static void $noinline$stackOverflow(Main m, boolean isSecondInvocation) {
      // If we are running in non-JIT mode, or were unlucky enough to get this method
      // already JITted, just return the expected value.
      if (!isInInterpreter("$noinline$stackOverflow")) {
        return;
      }
  
      // We need a ProfilingInfo object to populate the 'otherInlineCache' call.
      ensureHasProfilingInfo("$noinline$stackOverflow");
  
      if (isSecondInvocation) {
        // Ensure we have an OSR code and we jump to it.
        while (!isInOsrCode("$noinline$stackOverflow")) {}
      }
  
      for (int i = 0; i < (isSecondInvocation ? 10000000 : 1); ++i) {
        // The first invocation of $noinline$stackOverflow will populate the inline
        // cache with Main. The second invocation of the method, will see a SubMain
        // and will therefore trigger deoptimization.
        m.otherInlineCache();
      }
    }
  
    public static void $opt$noinline$testOsrInlineLoop(String[] args) {
      // Regression test for inlining a method with a loop to a method without a loop in OSR mode.
      if (doThrow) throw new Error();
      assertIntEquals(12, $opt$inline$testRemoveSuspendCheck(12, 5));
      // Since we cannot have a loop directly in this method, we need to force the OSR
      // compilation from native code.
      ensureHasProfilingInfo("$opt$noinline$testOsrInlineLoop");
      ensureHasOsrCode("$opt$noinline$testOsrInlineLoop");
    }
  
    public static int $opt$inline$testRemoveSuspendCheck(int x, int y) {
      // For this test we need an inlined loop and have DCE re-run loop analysis
      // after inlining.
      while (y > 0) {
        while ($opt$inline$inlineFalse() || !$opt$inline$inlineTrue()) {
          x++;
        }
        y--;
      }
      return x;
    }
  
    public static boolean $opt$inline$inlineTrue() {
      return true;
    }
  
    public static boolean $opt$inline$inlineFalse() {
      return false;
    }
  
    public static void assertIntEquals(int expected, int result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    public static native boolean isInOsrCode(String methodName);
    public static native boolean isInInterpreter(String methodName);
    public static native void ensureHasProfilingInfo(String methodName);
    public static native void ensureHasOsrCode(String methodName);
  
    public static boolean doThrow = false;
  }
  
  class SubMain extends Main {
    public Class returnClass() {
      return SubMain.class;
    }
  
    public Main inlineCache() {
      return new SubMain();
    }
  
    public Main inlineCache2() {
      return new SubMain();
    }
  
    public void otherInlineCache() {
      return;
    }
  }
  