<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Navigates the browser to server and client redirect pages and makes sure
// that the correct redirects are reflected in the history database. Errors
// here might indicate that WebKit changed the calls our glue layer gets in
// the case of redirects. It may also mean problems with the history system.

#include "base/file_util.h"
#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/view_ids.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"
#include "views/event.h"

namespace {

class RedirectTest : public UITest {
 public:
  RedirectTest()
      : test_server_(net::TestServer::TYPE_HTTP,
                     FilePath(FILE_PATH_LITERAL("chrome/test/data"))) {
  }

 protected:
  net::TestServer test_server_;
};

// Tests a single server redirect
TEST_F(RedirectTest, Server) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL first_url = test_server_.GetURL(
      "server-redirect?" + final_url.spec());

  NavigateToURL(first_url);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests a single client redirect.
TEST_F(RedirectTest, Client) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + final_url.spec());

  // The client redirect appears as two page visits in the browser.
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());

  // The address bar should display the final URL.
  GURL tab_url;
  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
  EXPECT_TRUE(final_url == tab_url);

  // Navigate one more time.
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  // The address bar should still display the final URL.
  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
  EXPECT_TRUE(final_url == tab_url);
}

TEST_F(RedirectTest, ClientEmptyReferer) {
  ASSERT_TRUE(test_server_.Start());

  // Create the file contents, which will do a redirect to the
  // test server.
  GURL final_url = test_server_.GetURL(std::string());
  ASSERT_TRUE(final_url.is_valid());
  std::string file_redirect_contents = StringPrintf(
      ""
      ""
      ""
      "",
      final_url.spec().c_str());

  // Write the contents to a temporary file.
  ScopedTempDir temp_directory;
  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
  FilePath temp_file;
  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(),
                                                  &temp_file));
  ASSERT_EQ(static_cast<int>(file_redirect_contents.size()),
            file_util::WriteFile(temp_file,
                                 file_redirect_contents.data(),
                                 file_redirect_contents.size()));

  // Navigate to the file through the browser. The client redirect will appear
  // as two page visits in the browser.
  GURL first_url = net::FilePathToFileURL(temp_file);
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  std::vector<GURL> redirects;
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests to make sure a location change when a pending redirect exists isn't
// flagged as a redirect.
#if defined(OS_MACOSX)
// SimulateOSClick is broken on the Mac: http://crbug.com/45162
#define MAYBE_ClientCancelled DISABLED_ClientCancelled
#elif defined(OS_WIN)
// http://crbug.com/53091
#define MAYBE_ClientCancelled FAILS_ClientCancelled
#else
#define MAYBE_ClientCancelled ClientCancelled
#endif

TEST_F(RedirectTest, MAYBE_ClientCancelled) {
  FilePath first_path(test_data_directory_);
  first_path = first_path.AppendASCII("cancelled_redirect_test.html");
  ASSERT_TRUE(file_util::AbsolutePath(&first_path));
  GURL first_url = net::FilePathToFileURL(first_path);

  NavigateToURLBlockUntilNavigationsComplete(first_url, 1);

  scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(0);
  ASSERT_TRUE(browser.get());
  scoped_refptr<WindowProxy> window = browser->GetWindow();
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  int64 last_nav_time = 0;
  EXPECT_TRUE(tab_proxy->GetLastNavigationTime(&last_nav_time));
  // Simulate a click to force to make a user-initiated location change;
  // otherwise, a non user-initiated in-page location change will be treated
  // as client redirect and the redirect will be recoreded, which can cause
  // this test failed.
  gfx::Rect tab_view_bounds;
  ASSERT_TRUE(browser->BringToFront());
  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER, &tab_view_bounds,
                                    true));
  ASSERT_TRUE(
      window->SimulateOSClick(tab_view_bounds.CenterPoint(),
                              views::Event::EF_LEFT_BUTTON_DOWN));
  EXPECT_TRUE(tab_proxy->WaitForNavigation(last_nav_time));

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  // There should be no redirects from first_url, because the anchor location
  // change that occurs should not be flagged as a redirect and the meta-refresh
  // won't have fired yet.
  ASSERT_EQ(0U, redirects.size());
  GURL current_url;
  ASSERT_TRUE(tab_proxy->GetCurrentURL(&current_url));

  // Need to test final path and ref separately since constructing a file url
  // containing an anchor using FilePathToFileURL will escape the anchor as
  // %23, but in current_url the anchor will be '#'.
  std::string final_ref = "myanchor";
  FilePath current_path;
  ASSERT_TRUE(net::FileURLToFilePath(current_url, &current_path));
  ASSERT_TRUE(file_util::AbsolutePath(&current_path));
  // Path should remain unchanged.
  EXPECT_EQ(StringToLowerASCII(first_path.value()),
            StringToLowerASCII(current_path.value()));
  EXPECT_EQ(final_ref, current_url.ref());
}

// Tests a client->server->server redirect
TEST_F(RedirectTest, ClientServerServer) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL next_to_last = test_server_.GetURL(
      "server-redirect?" + final_url.spec());
  GURL second_url = test_server_.GetURL(
      "server-redirect?" + next_to_last.spec());
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + second_url.spec());
  std::vector<GURL> redirects;

  // We need the sleep for the client redirects, because it appears as two
  // page visits in the browser.
  NavigateToURL(first_url);

  for (int i = 0; i < 10; ++i) {
    PlatformThread::Sleep(sleep_timeout_ms());
    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
    ASSERT_TRUE(tab_proxy.get());
    ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
    if (!redirects.empty())
      break;
  }

  ASSERT_EQ(3U, redirects.size());
  EXPECT_EQ(second_url.spec(), redirects[0].spec());
  EXPECT_EQ(next_to_last.spec(), redirects[1].spec());
  EXPECT_EQ(final_url.spec(), redirects[2].spec());
}

// Tests that the "#reference" gets preserved across server redirects.
TEST_F(RedirectTest, ServerReference) {
  ASSERT_TRUE(test_server_.Start());

  const std::string ref("reference");

  GURL final_url = test_server_.GetURL(std::string());
  GURL initial_url = test_server_.GetURL(
      "server-redirect?" + final_url.spec() + "#" + ref);

  NavigateToURL(initial_url);

  GURL url = GetActiveTabURL();
  EXPECT_EQ(ref, url.ref());
}

// Test that redirect from http:// to file:// :
// A) does not crash the browser or confuse the redirect chain, see bug 1080873
// B) does not take place.
TEST_F(RedirectTest, NoHttpToFile) {
  ASSERT_TRUE(test_server_.Start());
  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("http_to_file.html");
  GURL file_url = net::FilePathToFileURL(test_file);

  GURL initial_url = test_server_.GetURL(
      "client-redirect?" + file_url.spec());

  NavigateToURL(initial_url);
  // UITest will check for crashes. We make sure the title doesn't match the
  // title from the file, because the nav should not have taken place.
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  std::wstring actual_title;
  ASSERT_TRUE(tab_proxy->GetTabTitle(&actual_title));
  EXPECT_NE("File!", WideToUTF8(actual_title));
}

// Ensures that non-user initiated location changes (within page) are
// flagged as client redirects. See bug 1139823.
TEST_F(RedirectTest, ClientFragments) {
  ASSERT_TRUE(test_server_.Start());

  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("ref_redirect.html");
  GURL first_url = net::FilePathToFileURL(test_file);
  std::vector<GURL> redirects;

  NavigateToURL(first_url);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
  EXPECT_EQ(1U, redirects.size());
  EXPECT_EQ(first_url.spec() + "#myanchor", redirects[0].spec());
}

// TODO(timsteele): This is disabled because our current testserver can't
// handle multiple requests in parallel, making it hang on the first request
// to /slow?60. It's unable to serve our second request for files/title2.html
// until /slow? completes, which doesn't give the desired behavior. We could
// alternatively load the second page from disk, but we would need to start
// the browser for this testcase with --process-per-tab, and I don't think
// we can do this at test-case-level granularity at the moment.
// http://crbug.com/45056
TEST_F(RedirectTest,
       DISABLED_ClientCancelledByNewNavigationAfterProvisionalLoad) {
  // We want to initiate a second navigation after the provisional load for
  // the client redirect destination has started, but before this load is
  // committed. To achieve this, we tell the browser to load a slow page,
  // which causes it to start a provisional load, and while it is waiting
  // for the response (which means it hasn't committed the load for the client
  // redirect destination page yet), we issue a new navigation request.
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL("files/title2.html");
  GURL slow = test_server_.GetURL("slow?60");
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + slow.spec());
  std::vector<GURL> redirects;

  NavigateToURL(first_url);
  // We don't sleep here - the first navigation won't have been committed yet
  // because we told the server to wait a minute. This means the browser has
  // started it's provisional load for the client redirect destination page but
  // hasn't completed. Our time is now!
  NavigateToURL(final_url);

  std::wstring tab_title;
  std::wstring final_url_title = UTF8ToWide("Title Of Awesomeness");
  // Wait till the final page has been loaded.
  for (int i = 0; i < 10; ++i) {
    PlatformThread::Sleep(sleep_timeout_ms());
    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
    ASSERT_TRUE(tab_proxy.get());
    ASSERT_TRUE(tab_proxy->GetTabTitle(&tab_title));
    if (tab_title == final_url_title) {
      ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
      break;
    }
  }

  // Check to make sure the navigation did in fact take place and we are
  // at the expected page.
  EXPECT_EQ(final_url_title, tab_title);

  bool final_navigation_not_redirect = true;
  // Check to make sure our request for files/title2.html doesn't get flagged
  // as a client redirect from the first (/client-redirect?) page.
  for (std::vector<GURL>::iterator it = redirects.begin();
       it != redirects.end(); ++it) {
    if (final_url.spec() == it->spec()) {
      final_navigation_not_redirect = false;
      break;
    }
  }
  EXPECT_TRUE(final_navigation_not_redirect);
}

}  // namespace
<commit_msg>Marking the RedirectTest.ClientEmptyReferer as flaky.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Navigates the browser to server and client redirect pages and makes sure
// that the correct redirects are reflected in the history database. Errors
// here might indicate that WebKit changed the calls our glue layer gets in
// the case of redirects. It may also mean problems with the history system.

#include "base/file_util.h"
#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/view_ids.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"
#include "views/event.h"

namespace {

class RedirectTest : public UITest {
 public:
  RedirectTest()
      : test_server_(net::TestServer::TYPE_HTTP,
                     FilePath(FILE_PATH_LITERAL("chrome/test/data"))) {
  }

 protected:
  net::TestServer test_server_;
};

// Tests a single server redirect
TEST_F(RedirectTest, Server) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL first_url = test_server_.GetURL(
      "server-redirect?" + final_url.spec());

  NavigateToURL(first_url);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests a single client redirect.
TEST_F(RedirectTest, Client) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + final_url.spec());

  // The client redirect appears as two page visits in the browser.
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());

  // The address bar should display the final URL.
  GURL tab_url;
  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
  EXPECT_TRUE(final_url == tab_url);

  // Navigate one more time.
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  // The address bar should still display the final URL.
  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
  EXPECT_TRUE(final_url == tab_url);
}

// http://code.google.com/p/chromium/issues/detail?id=62772
TEST_F(RedirectTest, FLAKY_ClientEmptyReferer) {
  ASSERT_TRUE(test_server_.Start());

  // Create the file contents, which will do a redirect to the
  // test server.
  GURL final_url = test_server_.GetURL(std::string());
  ASSERT_TRUE(final_url.is_valid());
  std::string file_redirect_contents = StringPrintf(
      ""
      ""
      ""
      "",
      final_url.spec().c_str());

  // Write the contents to a temporary file.
  ScopedTempDir temp_directory;
  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
  FilePath temp_file;
  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(),
                                                  &temp_file));
  ASSERT_EQ(static_cast<int>(file_redirect_contents.size()),
            file_util::WriteFile(temp_file,
                                 file_redirect_contents.data(),
                                 file_redirect_contents.size()));

  // Navigate to the file through the browser. The client redirect will appear
  // as two page visits in the browser.
  GURL first_url = net::FilePathToFileURL(temp_file);
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  std::vector<GURL> redirects;
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests to make sure a location change when a pending redirect exists isn't
// flagged as a redirect.
#if defined(OS_MACOSX)
// SimulateOSClick is broken on the Mac: http://crbug.com/45162
#define MAYBE_ClientCancelled DISABLED_ClientCancelled
#elif defined(OS_WIN)
// http://crbug.com/53091
#define MAYBE_ClientCancelled FAILS_ClientCancelled
#else
#define MAYBE_ClientCancelled ClientCancelled
#endif

TEST_F(RedirectTest, MAYBE_ClientCancelled) {
  FilePath first_path(test_data_directory_);
  first_path = first_path.AppendASCII("cancelled_redirect_test.html");
  ASSERT_TRUE(file_util::AbsolutePath(&first_path));
  GURL first_url = net::FilePathToFileURL(first_path);

  NavigateToURLBlockUntilNavigationsComplete(first_url, 1);

  scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(0);
  ASSERT_TRUE(browser.get());
  scoped_refptr<WindowProxy> window = browser->GetWindow();
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  int64 last_nav_time = 0;
  EXPECT_TRUE(tab_proxy->GetLastNavigationTime(&last_nav_time));
  // Simulate a click to force to make a user-initiated location change;
  // otherwise, a non user-initiated in-page location change will be treated
  // as client redirect and the redirect will be recoreded, which can cause
  // this test failed.
  gfx::Rect tab_view_bounds;
  ASSERT_TRUE(browser->BringToFront());
  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER, &tab_view_bounds,
                                    true));
  ASSERT_TRUE(
      window->SimulateOSClick(tab_view_bounds.CenterPoint(),
                              views::Event::EF_LEFT_BUTTON_DOWN));
  EXPECT_TRUE(tab_proxy->WaitForNavigation(last_nav_time));

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  // There should be no redirects from first_url, because the anchor location
  // change that occurs should not be flagged as a redirect and the meta-refresh
  // won't have fired yet.
  ASSERT_EQ(0U, redirects.size());
  GURL current_url;
  ASSERT_TRUE(tab_proxy->GetCurrentURL(&current_url));

  // Need to test final path and ref separately since constructing a file url
  // containing an anchor using FilePathToFileURL will escape the anchor as
  // %23, but in current_url the anchor will be '#'.
  std::string final_ref = "myanchor";
  FilePath current_path;
  ASSERT_TRUE(net::FileURLToFilePath(current_url, &current_path));
  ASSERT_TRUE(file_util::AbsolutePath(&current_path));
  // Path should remain unchanged.
  EXPECT_EQ(StringToLowerASCII(first_path.value()),
            StringToLowerASCII(current_path.value()));
  EXPECT_EQ(final_ref, current_url.ref());
}

// Tests a client->server->server redirect
TEST_F(RedirectTest, ClientServerServer) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL next_to_last = test_server_.GetURL(
      "server-redirect?" + final_url.spec());
  GURL second_url = test_server_.GetURL(
      "server-redirect?" + next_to_last.spec());
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + second_url.spec());
  std::vector<GURL> redirects;

  // We need the sleep for the client redirects, because it appears as two
  // page visits in the browser.
  NavigateToURL(first_url);

  for (int i = 0; i < 10; ++i) {
    PlatformThread::Sleep(sleep_timeout_ms());
    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
    ASSERT_TRUE(tab_proxy.get());
    ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
    if (!redirects.empty())
      break;
  }

  ASSERT_EQ(3U, redirects.size());
  EXPECT_EQ(second_url.spec(), redirects[0].spec());
  EXPECT_EQ(next_to_last.spec(), redirects[1].spec());
  EXPECT_EQ(final_url.spec(), redirects[2].spec());
}

// Tests that the "#reference" gets preserved across server redirects.
TEST_F(RedirectTest, ServerReference) {
  ASSERT_TRUE(test_server_.Start());

  const std::string ref("reference");

  GURL final_url = test_server_.GetURL(std::string());
  GURL initial_url = test_server_.GetURL(
      "server-redirect?" + final_url.spec() + "#" + ref);

  NavigateToURL(initial_url);

  GURL url = GetActiveTabURL();
  EXPECT_EQ(ref, url.ref());
}

// Test that redirect from http:// to file:// :
// A) does not crash the browser or confuse the redirect chain, see bug 1080873
// B) does not take place.
TEST_F(RedirectTest, NoHttpToFile) {
  ASSERT_TRUE(test_server_.Start());
  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("http_to_file.html");
  GURL file_url = net::FilePathToFileURL(test_file);

  GURL initial_url = test_server_.GetURL(
      "client-redirect?" + file_url.spec());

  NavigateToURL(initial_url);
  // UITest will check for crashes. We make sure the title doesn't match the
  // title from the file, because the nav should not have taken place.
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  std::wstring actual_title;
  ASSERT_TRUE(tab_proxy->GetTabTitle(&actual_title));
  EXPECT_NE("File!", WideToUTF8(actual_title));
}

// Ensures that non-user initiated location changes (within page) are
// flagged as client redirects. See bug 1139823.
TEST_F(RedirectTest, ClientFragments) {
  ASSERT_TRUE(test_server_.Start());

  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("ref_redirect.html");
  GURL first_url = net::FilePathToFileURL(test_file);
  std::vector<GURL> redirects;

  NavigateToURL(first_url);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
  EXPECT_EQ(1U, redirects.size());
  EXPECT_EQ(first_url.spec() + "#myanchor", redirects[0].spec());
}

// TODO(timsteele): This is disabled because our current testserver can't
// handle multiple requests in parallel, making it hang on the first request
// to /slow?60. It's unable to serve our second request for files/title2.html
// until /slow? completes, which doesn't give the desired behavior. We could
// alternatively load the second page from disk, but we would need to start
// the browser for this testcase with --process-per-tab, and I don't think
// we can do this at test-case-level granularity at the moment.
// http://crbug.com/45056
TEST_F(RedirectTest,
       DISABLED_ClientCancelledByNewNavigationAfterProvisionalLoad) {
  // We want to initiate a second navigation after the provisional load for
  // the client redirect destination has started, but before this load is
  // committed. To achieve this, we tell the browser to load a slow page,
  // which causes it to start a provisional load, and while it is waiting
  // for the response (which means it hasn't committed the load for the client
  // redirect destination page yet), we issue a new navigation request.
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL("files/title2.html");
  GURL slow = test_server_.GetURL("slow?60");
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + slow.spec());
  std::vector<GURL> redirects;

  NavigateToURL(first_url);
  // We don't sleep here - the first navigation won't have been committed yet
  // because we told the server to wait a minute. This means the browser has
  // started it's provisional load for the client redirect destination page but
  // hasn't completed. Our time is now!
  NavigateToURL(final_url);

  std::wstring tab_title;
  std::wstring final_url_title = UTF8ToWide("Title Of Awesomeness");
  // Wait till the final page has been loaded.
  for (int i = 0; i < 10; ++i) {
    PlatformThread::Sleep(sleep_timeout_ms());
    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
    ASSERT_TRUE(tab_proxy.get());
    ASSERT_TRUE(tab_proxy->GetTabTitle(&tab_title));
    if (tab_title == final_url_title) {
      ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
      break;
    }
  }

  // Check to make sure the navigation did in fact take place and we are
  // at the expected page.
  EXPECT_EQ(final_url_title, tab_title);

  bool final_navigation_not_redirect = true;
  // Check to make sure our request for files/title2.html doesn't get flagged
  // as a client redirect from the first (/client-redirect?) page.
  for (std::vector<GURL>::iterator it = redirects.begin();
       it != redirects.end(); ++it) {
    if (final_url.spec() == it->spec()) {
      final_navigation_not_redirect = false;
      break;
    }
  }
  EXPECT_TRUE(final_navigation_not_redirect);
}

}  // namespace
<|endoftext|>


<commit_before>#pragma once
#include <Managers/TowerManager.h>
#include <Game.h>

void TowerManager::clearAllTowers() {
	for(int tileX = 0; tileX < tArrayRows ; ++tileX){
		for(int tileY = 0; tileY < tArrayCols ; ++tileY){
			if(tArray[tileX][tileY] != NULL) {
				delete tArray[tileX][tileY];
			}
		}
	}
}

void TowerManager::setArraySize(int mapWidth, int mapHeight) {
	this->tArrayRows = mapWidth;
	this->tArrayCols = mapHeight;

	//resize 1st dimension of vector to mapWidth (number of tiles width-wise)
	tArray.resize(mapWidth);

	//resize 2nd dimension of vector to mapHeight(number of tiles height-wise)
	for(int i = 0; i < tArrayRows; ++i)
		tArray.at(i).resize(tArrayCols);
}

bool TowerManager::isTileFree(int tileX, int tileY) {
	if(!outOfBound(tileX, tileY) && tArray[tileX][tileY] == NULL) {
		return true;
	}
	return false;
}

Tower* TowerManager::getTower(int tileX, int tileY) {
	if(!outOfBound(tileX, tileY) && tArray[tileX][tileY] != NULL)
		return tArray[tileX][tileY];
	return NULL;	// this line should never be reached
}

bool TowerManager::outOfBound(int tileX, int tileY) {
	return (!(-1 < tileX && tileX < tArrayRows) || !(-1 < tileY && tileY < tArrayCols));
}

Tower* TowerManager::buyTower(Tower::TowerType type, int tileX, int tileY) {
	if(!outOfBound(tileX, tileY) && isTileFree(tileX, tileY)) {
		switch(type) {
			case 0: {
				if(ShihTzu::buy_cost <= Game::player.getCash()) {
					TowerManager::tArray[tileX][tileY] = new ShihTzu(tileX, tileY);
					Game::player.spendCash(ShihTzu::buy_cost);
					cout << blue << tArray[tileX][tileY]->getName() << " bought for "
						<< ShihTzu::buy_cost
						<< " coins!" << white << std::endl;
				}
				else {
					cout << red << "Insufficient funds." << std::endl;
					return NULL;
				}
				break;
			}
			case 1: {
				if(Dalmatian::buy_cost <= Game::player.getCash()) {
					tArray[tileX][tileY] = new Dalmatian(tileX, tileY);
					Game::player.spendCash(Dalmatian::buy_cost);
					cout << blue << tArray[tileX][tileY]->getName() << " bought for "
						<< Dalmatian::buy_cost
						<< " coins!" << white << std::endl;
				}
				else {
					cout << red << "Insufficient funds." << std::endl;
					return NULL;
				}
				break;
			}
			case 2: {
				if(Bulldog::buy_cost <= Game::player.getCash()) {
					tArray[tileX][tileY] = new Bulldog(tileX, tileY);
					Game::player.spendCash(Bulldog::buy_cost);
					cout << blue << tArray[tileX][tileY]->getName() << " bought for "
						<< Bulldog::buy_cost
						<< " coins!" << white << std::endl;
				}
				else {
					cout << red << "Insufficient funds." << std::endl;
					return NULL;
				}
				break;
			}
		}
		return tArray[tileX][tileY];
	} else {
		std::cerr << red << "Error: There is already a tower on this cell." << std::endl;
	}
	return NULL;
}

void TowerManager::sellTower(int tileX, int tileY)  {
	if(!outOfBound(tileX, tileY) && isTileFree(tileX, tileY))
		return;
	Game::player.earnCash(tArray[tileX][tileY]->getSellCost());
	std::cout << blue << tArray[tileX][tileY]->getName() << " sold for "
		<< tArray[tileX][tileY]->getSellCost()
		<< " coins!" << white << std::endl;
	delete tArray[tileX][tileY];
	tArray[tileX][tileY] = NULL;
}

void TowerManager::displayTowerArray() {
	cout << white;
	for(int i = 0; i < tArrayCols; i++) {
		for(int j = 0; j < tArrayRows; j++) {
			if(isTileFree(j, i))
				cout << white << "O" << " ";
			else {
				switch(tArray[j][i]->getType()) {
					case 0:
						cout << blue << "W" << " ";
						break;
					case 1:
						cout << green << "A" << " ";
						break;
					case 2:
						cout << red << "C" << " ";
						break;
					default:
						cout << white << "X" << " ";
				}
			}

		}
		cout << std::endl;
	}
	cout << std::endl;
}

//draws tower to game window
void TowerManager::draw(sf::RenderWindow& gameWindow){
	for(int tileX = 0; tileX < tArrayRows ; ++tileX){
		for(int tileY = 0; tileY < tArrayCols ; ++tileY){
			if(tArray[tileX][tileY] != NULL) {
				tArray[tileX][tileY]->draw(gameWindow);
			}
		}
	}
}
<commit_msg>Fixed clearAllTower() method bug<commit_after>#pragma once
#include <Managers/TowerManager.h>
#include <Game.h>

void TowerManager::clearAllTowers() {
	std::cout << "CLEARALLTOWERS() CALLED" << std::endl;
	for(int tileX = 0; tileX < tArrayRows ; ++tileX){
		for(int tileY = 0; tileY < tArrayCols ; ++tileY){
			if(tArray[tileX][tileY] != NULL) {
				delete tArray[tileX][tileY];
				tArray[tileX][tileY] = nullptr;
			}
		}
	}
}

void TowerManager::setArraySize(int mapWidth, int mapHeight) {
	this->tArrayRows = mapWidth;
	this->tArrayCols = mapHeight;

	//resize 1st dimension of vector to mapWidth (number of tiles width-wise)
	tArray.resize(mapWidth);

	//resize 2nd dimension of vector to mapHeight(number of tiles height-wise)
	for(int i = 0; i < tArrayRows; ++i)
		tArray.at(i).resize(tArrayCols);
}

bool TowerManager::isTileFree(int tileX, int tileY) {
	if(!outOfBound(tileX, tileY) && tArray[tileX][tileY] == NULL) {
		return true;
	}
	return false;
}

Tower* TowerManager::getTower(int tileX, int tileY) {
	if(!outOfBound(tileX, tileY) && tArray[tileX][tileY] != NULL)
		return tArray[tileX][tileY];
	return NULL;	// this line should never be reached
}

bool TowerManager::outOfBound(int tileX, int tileY) {
	return (!(-1 < tileX && tileX < tArrayRows) || !(-1 < tileY && tileY < tArrayCols));
}

Tower* TowerManager::buyTower(Tower::TowerType type, int tileX, int tileY) {
	if(!outOfBound(tileX, tileY) && isTileFree(tileX, tileY)) {
		switch(type) {
			case 0: {
				if(ShihTzu::buy_cost <= Game::player.getCash()) {
					TowerManager::tArray[tileX][tileY] = new ShihTzu(tileX, tileY);
					Game::player.spendCash(ShihTzu::buy_cost);
					cout << blue << tArray[tileX][tileY]->getName() << " bought for "
						<< ShihTzu::buy_cost
						<< " coins!" << white << std::endl;
				}
				else {
					cout << red << "Insufficient funds." << std::endl;
					return NULL;
				}
				break;
			}
			case 1: {
				if(Dalmatian::buy_cost <= Game::player.getCash()) {
					tArray[tileX][tileY] = new Dalmatian(tileX, tileY);
					Game::player.spendCash(Dalmatian::buy_cost);
					cout << blue << tArray[tileX][tileY]->getName() << " bought for "
						<< Dalmatian::buy_cost
						<< " coins!" << white << std::endl;
				}
				else {
					cout << red << "Insufficient funds." << std::endl;
					return NULL;
				}
				break;
			}
			case 2: {
				if(Bulldog::buy_cost <= Game::player.getCash()) {
					tArray[tileX][tileY] = new Bulldog(tileX, tileY);
					Game::player.spendCash(Bulldog::buy_cost);
					cout << blue << tArray[tileX][tileY]->getName() << " bought for "
						<< Bulldog::buy_cost
						<< " coins!" << white << std::endl;
				}
				else {
					cout << red << "Insufficient funds." << std::endl;
					return NULL;
				}
				break;
			}
		}
		return tArray[tileX][tileY];
	} else {
		std::cerr << red << "Error: There is already a tower on this cell." << std::endl;
	}
	return NULL;
}

void TowerManager::sellTower(int tileX, int tileY)  {
	if(!outOfBound(tileX, tileY) && isTileFree(tileX, tileY))
		return;
	Game::player.earnCash(tArray[tileX][tileY]->getSellCost());
	std::cout << blue << tArray[tileX][tileY]->getName() << " sold for "
		<< tArray[tileX][tileY]->getSellCost()
		<< " coins!" << white << std::endl;
	delete tArray[tileX][tileY];
	tArray[tileX][tileY] = NULL;
}

void TowerManager::displayTowerArray() {
	cout << white;
	for(int i = 0; i < tArrayCols; i++) {
		for(int j = 0; j < tArrayRows; j++) {
			if(isTileFree(j, i))
				cout << white << "O" << " ";
			else {
				switch(tArray[j][i]->getType()) {
					case 0:
						cout << blue << "W" << " ";
						break;
					case 1:
						cout << green << "A" << " ";
						break;
					case 2:
						cout << red << "C" << " ";
						break;
					default:
						cout << white << "X" << " ";
				}
			}

		}
		cout << std::endl;
	}
	cout << std::endl;
}

//draws tower to game window
void TowerManager::draw(sf::RenderWindow& gameWindow){
	for(int tileX = 0; tileX < tArrayRows ; ++tileX){
		for(int tileY = 0; tileY < tArrayCols ; ++tileY){
			if(tArray[tileX][tileY] != NULL) {
				tArray[tileX][tileY]->draw(gameWindow);
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
    Copyright(c) Microsoft Open Technologies, Inc. All rights reserved.

    The MIT License(MIT)

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files(the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions :

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

#include "pch.h"
#include "StartupTask.h"
#include <ppltasks.h>
#include "v8.h"
#include "node.h"
#include "Logger.h"

using namespace nodeuwp;

using namespace Platform;
using namespace Windows::ApplicationModel::Background;
using namespace concurrency;
using namespace Windows::UI::Core;
using namespace Windows::Storage;
using namespace Windows::Data::Xml::Dom;
using namespace Platform::Collections;

// startupinfo.xml is used by Visual Studio to pass arguments to Node (see StartNode method).
// It's updated through Node.js UWP project properties and packaged in the project appx.
#define STARTUP_FILE L"startupinfo.xml"

std::shared_ptr<char> PlatformStringToChar(const wchar_t* str, int strSize)
{
	// Calculate the needed buffer size
	DWORD bufferSize = WideCharToMultiByte(CP_UTF8,
		0,
		str,
		strSize,
		nullptr,
		0,
		nullptr,
		nullptr);

	if (bufferSize == 0)
	{
		throw ref new ::Platform::Exception(GetLastError(), L"Failed to convert Platform string to utf8 string");
	}

	std::shared_ptr<char> buffer(new char[bufferSize + 1], [](char* ptr) { delete[] ptr; });
	buffer.get()[bufferSize] = '\0';
	// Do the actual conversion
	WideCharToMultiByte(CP_UTF8,
		0,
		str,
		strSize,
		buffer.get(),
		bufferSize,
		nullptr,
		nullptr);

	return buffer;
}

void PopulateArgsVector(std::vector<std::shared_ptr<char>> &argVector, XmlNodeList^ argNodes)
{
	if (argNodes != nullptr)
	{
		IXmlNode^ textNode = argNodes->GetAt(0)->FirstChild;

		if (nullptr == textNode)
			return;

		std::wstring s(((Platform::String^)textNode->NodeValue)->Data());
		std::wstring delimiter = L" ";

		size_t pos = 0;
		std::wstring token;
		std::shared_ptr<char> argChar;
		while ((pos = s.find(delimiter)) != std::wstring::npos) {
			token = s.substr(0, pos);
			argChar = PlatformStringToChar(token.c_str(), token.size());
			argVector.push_back(argChar);
			s.erase(0, pos + delimiter.length());
		}
		argChar = PlatformStringToChar(s.c_str(), s.size());
		argVector.push_back(argChar);
	}
}

void StartupTask::Run(IBackgroundTaskInstance^ taskInstance)
{
	BackgroundTaskDeferral^ deferral = taskInstance->GetDeferral();

	auto installationLocation = Windows::ApplicationModel::Package::Current->InstalledLocation;

	task<StorageFile^> getStartupInfo(installationLocation->GetFileAsync(STARTUP_FILE));

	getStartupInfo.then([=](StorageFile^ storageFile)
	{
		task<XmlDocument^> getStartupInfoXml(XmlDocument::LoadFromFileAsync(storageFile));

		getStartupInfoXml.then([=](XmlDocument^ startupInfoXml)
		{
			std::vector<std::shared_ptr<char>> argumentVector;
			bool useLogger = true; //TODO: set value from a property in Visual Studio

			std::shared_ptr<char> argChar = PlatformStringToChar(L" ", 1);
			argumentVector.push_back(argChar);

			XmlNodeList^ argumentNodes = startupInfoXml->SelectNodes(L"StartupInfo/NodeOptions");
			PopulateArgsVector(argumentVector, argumentNodes);

			argumentNodes = startupInfoXml->SelectNodes(L"StartupInfo/Script");
			PopulateArgsVector(argumentVector, argumentNodes);

			argumentNodes = startupInfoXml->SelectNodes(L"StartupInfo/ScriptArgs");
			PopulateArgsVector(argumentVector, argumentNodes);

			int argc = argumentVector.size();

			std::shared_ptr<char*> argv;
			argv.reset(new char*[argc], [](char** ptr) { delete[] ptr; });
			for (unsigned int i = 0; i < argumentVector.size(); ++i)
			{
				argv.get()[i] = (argumentVector[i]).get();
			}

			if (!useLogger)
			{
				node::Start(argc, argv.get());
			}
			else
			{
				node::Start(argc, argv.get(), &Logger::GetInstance());
			}
			deferral->Complete();
		});
	});
}
<commit_msg>Adding ability to turn logging off or on<commit_after>/*
    Copyright(c) Microsoft Open Technologies, Inc. All rights reserved.

    The MIT License(MIT)

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files(the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions :

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

#include "pch.h"
#include "StartupTask.h"
#include <ppltasks.h>
#include "v8.h"
#include "node.h"
#include "Logger.h"

using namespace nodeuwp;

using namespace Platform;
using namespace Windows::ApplicationModel::Background;
using namespace concurrency;
using namespace Windows::UI::Core;
using namespace Windows::Storage;
using namespace Windows::Data::Xml::Dom;
using namespace Platform::Collections;

// startupinfo.xml is used by Visual Studio to pass arguments to Node (see StartNode method).
// It's updated through Node.js UWP project properties and packaged in the project appx.
#define STARTUP_FILE L"startupinfo.xml"

// If --use-logger argument is passed to to Node, console.* methods will redirect
// output to a file (nodeuwp.log) in this applications local storage folder.
bool useLogger = false;

std::shared_ptr<char> PlatformStringToChar(const wchar_t* str, int strSize)
{
	// Calculate the needed buffer size
	DWORD bufferSize = WideCharToMultiByte(CP_UTF8,
		0,
		str,
		strSize,
		nullptr,
		0,
		nullptr,
		nullptr);

	if (bufferSize == 0)
	{
		throw ref new ::Platform::Exception(GetLastError(), L"Failed to convert Platform string to utf8 string");
	}

	std::shared_ptr<char> buffer(new char[bufferSize + 1], [](char* ptr) { delete[] ptr; });
	buffer.get()[bufferSize] = '\0';
	// Do the actual conversion
	WideCharToMultiByte(CP_UTF8,
		0,
		str,
		strSize,
		buffer.get(),
		bufferSize,
		nullptr,
		nullptr);

	return buffer;
}

void PopulateArgsVector(std::vector<std::shared_ptr<char>> &argVector, XmlNodeList^ argNodes)
{
	if (argNodes != nullptr)
	{
		IXmlNode^ textNode = argNodes->GetAt(0)->FirstChild;

		if (nullptr == textNode)
			return;

		std::wstring s(((Platform::String^)textNode->NodeValue)->Data());
		std::wstring delimiter = L" ";

		size_t pos = 0;
		std::wstring token;
		std::shared_ptr<char> argChar;
		while ((pos = s.find(delimiter)) != std::wstring::npos)
		{
			token = s.substr(0, pos);
			argChar = PlatformStringToChar(token.c_str(), token.size());
			if (0 == token.compare(L"--use-logger"))
			{
				useLogger = true;
			}
			else
			{
				argVector.push_back(argChar);
			}
			s.erase(0, pos + delimiter.length());
		}
		if (0 == s.compare(L"--use-logger"))
		{
			useLogger = true;
			return;
		}
		argChar = PlatformStringToChar(s.c_str(), s.size());
		argVector.push_back(argChar);
	}
}

void StartupTask::Run(IBackgroundTaskInstance^ taskInstance)
{
	BackgroundTaskDeferral^ deferral = taskInstance->GetDeferral();

	auto installationLocation = Windows::ApplicationModel::Package::Current->InstalledLocation;

	task<StorageFile^> getStartupInfo(installationLocation->GetFileAsync(STARTUP_FILE));

	getStartupInfo.then([=](StorageFile^ storageFile)
	{
		task<XmlDocument^> getStartupInfoXml(XmlDocument::LoadFromFileAsync(storageFile));

		getStartupInfoXml.then([=](XmlDocument^ startupInfoXml)
		{
			std::vector<std::shared_ptr<char>> argumentVector;

			std::shared_ptr<char> argChar = PlatformStringToChar(L" ", 1);
			argumentVector.push_back(argChar);

			XmlNodeList^ argumentNodes = startupInfoXml->SelectNodes(L"StartupInfo/NodeOptions");
			PopulateArgsVector(argumentVector, argumentNodes);

			argumentNodes = startupInfoXml->SelectNodes(L"StartupInfo/Script");
			PopulateArgsVector(argumentVector, argumentNodes);

			argumentNodes = startupInfoXml->SelectNodes(L"StartupInfo/ScriptArgs");
			PopulateArgsVector(argumentVector, argumentNodes);

			int argc = argumentVector.size();

			std::shared_ptr<char*> argv;
			argv.reset(new char*[argc], [](char** ptr) { delete[] ptr; });
			for (unsigned int i = 0; i < argumentVector.size(); ++i)
			{
				argv.get()[i] = (argumentVector[i]).get();
			}

			if (!useLogger)
			{
				node::Start(argc, argv.get());
			}
			else
			{
				node::Start(argc, argv.get(), &Logger::GetInstance());
			}
			deferral->Complete();
		});
	});
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2010 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/**
 * \file ir_function_inlining.cpp
 *
 * Replaces calls to functions with the body of the function.
 */

#include <inttypes.h>
#include "ir.h"
#include "ir_visitor.h"
#include "ir_function_inlining.h"
#include "ir_expression_flattening.h"
#include "glsl_types.h"
#include "program/hash_table.h"

static void
do_sampler_replacement(exec_list *instructions,
		       ir_variable *sampler,
		       ir_dereference *deref);

class ir_function_inlining_visitor : public ir_hierarchical_visitor {
public:
   ir_function_inlining_visitor()
   {
      progress = false;
	  current_function = NULL;
   }

   virtual ~ir_function_inlining_visitor()
   {
      /* empty */
   }

   virtual ir_visitor_status visit_enter(ir_expression *);
   virtual ir_visitor_status visit_enter(ir_call *);
   virtual ir_visitor_status visit_enter(ir_assignment *);
   virtual ir_visitor_status visit_enter(ir_return *);
   virtual ir_visitor_status visit_enter(ir_texture *);
   virtual ir_visitor_status visit_enter(ir_swizzle *);

   virtual ir_visitor_status visit_enter(ir_function_signature *sig)
   {
	   this->current_function = sig;
	   return ir_hierarchical_visitor::visit_enter(sig);
   }
   virtual ir_visitor_status visit_leave(ir_function_signature *sig)
   {
	   this->current_function = NULL;
	   return ir_hierarchical_visitor::visit_leave(sig);
   }

   ir_function_signature* current_function;
   bool progress;

};

bool
automatic_inlining_predicate(ir_instruction *ir)
{
   ir_call *call = ir->as_call();

   if (call && can_inline(call))
      return true;

   return false;
}

bool
do_function_inlining(exec_list *instructions)
{
   ir_function_inlining_visitor v;

   do_expression_flattening(instructions, automatic_inlining_predicate);

   v.run(instructions);

   return v.progress;
}

static void
replace_return_with_assignment(ir_instruction *ir, void *data)
{
   void *ctx = talloc_parent(ir);
   ir_variable *retval = (ir_variable *)data;
   ir_return *ret = ir->as_return();

   if (ret) {
      if (ret->value) {
	 ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
	 ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
      } else {
	 /* un-valued return has to be the last return, or we shouldn't
	  * have reached here. (see can_inline()).
	  */
	 assert(ret->next->is_tail_sentinel());
	 ret->remove();
      }
   }
}

static void rename_inlined_variable (ir_variable* new_var, ir_function_signature* func)
{
	// go through callee, see if we have any variables that match this one
	bool progress;
	int counter = 0;
	do
	{
		progress = false;
		foreach_iter(exec_list_iterator, iter, func->parameters)
		{
			const ir_variable* var = (const ir_variable*) iter.get();
			if (!strcmp(var->name, new_var->name))
			{
				progress = true;
				new_var->name = talloc_asprintf_append(const_cast<char*>(new_var->name), "_i%d", counter++);
			}
		}
		foreach_iter(exec_list_iterator, iter, func->body)
		{
			ir_instruction *ir = (ir_instruction*)iter.get();
			const ir_variable *var = ir->as_variable();
			if (!var)
				continue;
			if (!strcmp(var->name, new_var->name))
			{
				progress = true;
				new_var->name = talloc_asprintf_append(const_cast<char*>(new_var->name), "_i%d", counter++);
			}
		}
	}
	while (progress);
}

ir_rvalue *
ir_call::generate_inline(ir_instruction *next_ir, ir_function_signature* parent)
{
   void *ctx = talloc_parent(this);
   ir_variable **parameters;
   int num_parameters;
   int i;
   ir_variable *retval = NULL;
   struct hash_table *ht;

   ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);

   num_parameters = 0;
   foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
      num_parameters++;

   parameters = new ir_variable *[num_parameters];

   /* Generate storage for the return value. */
   if (this->callee->return_type) {
      retval = new(ctx) ir_variable(this->callee->return_type, "__retval",
				    ir_var_auto);
      next_ir->insert_before(retval);
   }

   /* Generate the declarations for the parameters to our inlined code,
    * and set up the mapping of real function body variables to ours.
    */
   i = 0;
   exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
   exec_list_iterator param_iter = this->actual_parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
      ir_rvalue *param = (ir_rvalue *) param_iter.get();

      /* Generate a new variable for the parameter. */
      if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
	 /* For samplers, we want the inlined sampler references
	  * referencing the passed in sampler variable, since that
	  * will have the location information, which an assignment of
	  * a sampler wouldn't.  Fix it up below.
	  */
	 parameters[i] = NULL;
      } else {
	 parameters[i] = sig_param->clone(ctx, ht);
	 parameters[i]->mode = ir_var_auto;
	 next_ir->insert_before(parameters[i]);
      }

      /* Move the actual param into our param variable if it's an 'in' type. */
      if (parameters[i] && (sig_param->mode == ir_var_in ||
			    sig_param->mode == ir_var_inout)) {
	 ir_assignment *assign;

	 assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
					 param, NULL);
	 next_ir->insert_before(assign);
      }

      sig_param_iter.next();
      param_iter.next();
   }

   exec_list new_instructions;

   /* Generate the inlined body of the function to a new list */
   foreach_iter(exec_list_iterator, iter, callee->body) {
      ir_instruction *ir = (ir_instruction *)iter.get();
      ir_instruction *new_ir = ir->clone(ctx, ht);
	  ir_variable *new_var = new_ir->as_variable();
	  if (new_var)
		  rename_inlined_variable (new_var, parent);

      new_instructions.push_tail(new_ir);
      visit_tree(new_ir, replace_return_with_assignment, retval);
   }

   /* If any samplers were passed in, replace any deref of the sampler
    * with a deref of the sampler argument.
    */
   param_iter = this->actual_parameters.iterator();
   sig_param_iter = this->callee->parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_instruction *const param = (ir_instruction *) param_iter.get();
      ir_variable *sig_param = (ir_variable *) sig_param_iter.get();

      if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
	 ir_dereference *deref = param->as_dereference();

	 assert(deref);
	 do_sampler_replacement(&new_instructions, sig_param, deref);
      }
   }

   /* Now push those new instructions in. */
   foreach_iter(exec_list_iterator, iter, new_instructions) {
      ir_instruction *ir = (ir_instruction *)iter.get();
      next_ir->insert_before(ir);
   }

   /* Copy back the value of any 'out' parameters from the function body
    * variables to our own.
    */
   i = 0;
   param_iter = this->actual_parameters.iterator();
   sig_param_iter = this->callee->parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_instruction *const param = (ir_instruction *) param_iter.get();
      const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();

      /* Move our param variable into the actual param if it's an 'out' type. */
      if (parameters[i] && (sig_param->mode == ir_var_out ||
			    sig_param->mode == ir_var_inout)) {
	 ir_assignment *assign;

	 assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
					 new(ctx) ir_dereference_variable(parameters[i]),
					 NULL);
	 next_ir->insert_before(assign);
      }

      param_iter.next();
      sig_param_iter.next();
   }

   delete [] parameters;

   hash_table_dtor(ht);

   if (retval)
      return new(ctx) ir_dereference_variable(retval);
   else
      return NULL;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_expression *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_return *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_texture *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_call *ir)
{
   if (can_inline(ir)) {
      /* If the call was part of some tree, then it should have been
       * flattened out or we shouldn't have seen it because of a
       * visit_continue_with_parent in this visitor.
       */
      assert(ir == base_ir);

      (void) ir->generate_inline(ir, this->current_function);
      ir->remove();
      this->progress = true;
   }

   return visit_continue;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
{
   ir_call *call = ir->rhs->as_call();
   if (!call || !can_inline(call))
      return visit_continue;

   /* generates the parameter setup, function body, and returns the return
    * value of the function
    */
   ir_rvalue *rhs = call->generate_inline(ir, this->current_function);
   assert(rhs);

   ir->rhs = rhs;
   this->progress = true;

   return visit_continue;
}

/**
 * Replaces references to the "sampler" variable with a clone of "deref."
 *
 * From the spec, samplers can appear in the tree as function
 * (non-out) parameters and as the result of array indexing and
 * structure field selection.  In our builtin implementation, they
 * also appear in the sampler field of an ir_tex instruction.
 */

class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
public:
   ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
   {
      this->sampler = sampler;
      this->deref = deref;
   }

   virtual ~ir_sampler_replacement_visitor()
   {
   }

   virtual ir_visitor_status visit_leave(ir_call *);
   virtual ir_visitor_status visit_leave(ir_dereference_array *);
   virtual ir_visitor_status visit_leave(ir_dereference_record *);
   virtual ir_visitor_status visit_leave(ir_texture *);

   void replace_deref(ir_dereference **deref);
   void replace_rvalue(ir_rvalue **rvalue);

   ir_variable *sampler;
   ir_dereference *deref;
};

void
ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
{
   ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
   if (deref_var && deref_var->var == this->sampler) {
      *deref = this->deref->clone(talloc_parent(*deref), NULL);
   }
}

void
ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
{
   if (!*rvalue)
      return;

   ir_dereference *deref = (*rvalue)->as_dereference();

   if (!deref)
      return;

   replace_deref(&deref);
   *rvalue = deref;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
{
   replace_deref(&ir->sampler);

   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
{
   replace_rvalue(&ir->array);
   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
{
   replace_rvalue(&ir->record);
   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
{
   foreach_iter(exec_list_iterator, iter, *ir) {
      ir_rvalue *param = (ir_rvalue *)iter.get();
      ir_rvalue *new_param = param;
      replace_rvalue(&new_param);

      if (new_param != param) {
	 param->replace_with(new_param);
      }
   }
   return visit_continue;
}

static void
do_sampler_replacement(exec_list *instructions,
		       ir_variable *sampler,
		       ir_dereference *deref)
{
   ir_sampler_replacement_visitor v(sampler, deref);

   visit_list_elements(&v, instructions);
}
<commit_msg>ir_function_inlining: looks like iterator next() were missing here<commit_after>/*
 * Copyright © 2010 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/**
 * \file ir_function_inlining.cpp
 *
 * Replaces calls to functions with the body of the function.
 */

#include <inttypes.h>
#include "ir.h"
#include "ir_visitor.h"
#include "ir_function_inlining.h"
#include "ir_expression_flattening.h"
#include "glsl_types.h"
#include "program/hash_table.h"

static void
do_sampler_replacement(exec_list *instructions,
		       ir_variable *sampler,
		       ir_dereference *deref);

class ir_function_inlining_visitor : public ir_hierarchical_visitor {
public:
   ir_function_inlining_visitor()
   {
      progress = false;
	  current_function = NULL;
   }

   virtual ~ir_function_inlining_visitor()
   {
      /* empty */
   }

   virtual ir_visitor_status visit_enter(ir_expression *);
   virtual ir_visitor_status visit_enter(ir_call *);
   virtual ir_visitor_status visit_enter(ir_assignment *);
   virtual ir_visitor_status visit_enter(ir_return *);
   virtual ir_visitor_status visit_enter(ir_texture *);
   virtual ir_visitor_status visit_enter(ir_swizzle *);

   virtual ir_visitor_status visit_enter(ir_function_signature *sig)
   {
	   this->current_function = sig;
	   return ir_hierarchical_visitor::visit_enter(sig);
   }
   virtual ir_visitor_status visit_leave(ir_function_signature *sig)
   {
	   this->current_function = NULL;
	   return ir_hierarchical_visitor::visit_leave(sig);
   }

   ir_function_signature* current_function;
   bool progress;

};

bool
automatic_inlining_predicate(ir_instruction *ir)
{
   ir_call *call = ir->as_call();

   if (call && can_inline(call))
      return true;

   return false;
}

bool
do_function_inlining(exec_list *instructions)
{
   ir_function_inlining_visitor v;

   do_expression_flattening(instructions, automatic_inlining_predicate);

   v.run(instructions);

   return v.progress;
}

static void
replace_return_with_assignment(ir_instruction *ir, void *data)
{
   void *ctx = talloc_parent(ir);
   ir_variable *retval = (ir_variable *)data;
   ir_return *ret = ir->as_return();

   if (ret) {
      if (ret->value) {
	 ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
	 ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
      } else {
	 /* un-valued return has to be the last return, or we shouldn't
	  * have reached here. (see can_inline()).
	  */
	 assert(ret->next->is_tail_sentinel());
	 ret->remove();
      }
   }
}

static void rename_inlined_variable (ir_variable* new_var, ir_function_signature* func)
{
	// go through callee, see if we have any variables that match this one
	bool progress;
	int counter = 0;
	do
	{
		progress = false;
		foreach_iter(exec_list_iterator, iter, func->parameters)
		{
			const ir_variable* var = (const ir_variable*) iter.get();
			if (!strcmp(var->name, new_var->name))
			{
				progress = true;
				new_var->name = talloc_asprintf_append(const_cast<char*>(new_var->name), "_i%d", counter++);
			}
		}
		foreach_iter(exec_list_iterator, iter, func->body)
		{
			ir_instruction *ir = (ir_instruction*)iter.get();
			const ir_variable *var = ir->as_variable();
			if (!var)
				continue;
			if (!strcmp(var->name, new_var->name))
			{
				progress = true;
				new_var->name = talloc_asprintf_append(const_cast<char*>(new_var->name), "_i%d", counter++);
			}
		}
	}
	while (progress);
}

ir_rvalue *
ir_call::generate_inline(ir_instruction *next_ir, ir_function_signature* parent)
{
   void *ctx = talloc_parent(this);
   ir_variable **parameters;
   int num_parameters;
   int i;
   ir_variable *retval = NULL;
   struct hash_table *ht;

   ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);

   num_parameters = 0;
   foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
      num_parameters++;

   parameters = new ir_variable *[num_parameters];

   /* Generate storage for the return value. */
   if (this->callee->return_type) {
      retval = new(ctx) ir_variable(this->callee->return_type, "__retval",
				    ir_var_auto);
      next_ir->insert_before(retval);
   }

   /* Generate the declarations for the parameters to our inlined code,
    * and set up the mapping of real function body variables to ours.
    */
   i = 0;
   exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
   exec_list_iterator param_iter = this->actual_parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
      ir_rvalue *param = (ir_rvalue *) param_iter.get();

      /* Generate a new variable for the parameter. */
      if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
	 /* For samplers, we want the inlined sampler references
	  * referencing the passed in sampler variable, since that
	  * will have the location information, which an assignment of
	  * a sampler wouldn't.  Fix it up below.
	  */
	 parameters[i] = NULL;
      } else {
	 parameters[i] = sig_param->clone(ctx, ht);
	 parameters[i]->mode = ir_var_auto;
	 next_ir->insert_before(parameters[i]);
      }

      /* Move the actual param into our param variable if it's an 'in' type. */
      if (parameters[i] && (sig_param->mode == ir_var_in ||
			    sig_param->mode == ir_var_inout)) {
	 ir_assignment *assign;

	 assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
					 param, NULL);
	 next_ir->insert_before(assign);
      }

      sig_param_iter.next();
      param_iter.next();
   }

   exec_list new_instructions;

   /* Generate the inlined body of the function to a new list */
   foreach_iter(exec_list_iterator, iter, callee->body) {
      ir_instruction *ir = (ir_instruction *)iter.get();
      ir_instruction *new_ir = ir->clone(ctx, ht);
	  ir_variable *new_var = new_ir->as_variable();
	  if (new_var)
		  rename_inlined_variable (new_var, parent);

      new_instructions.push_tail(new_ir);
      visit_tree(new_ir, replace_return_with_assignment, retval);
   }

   /* If any samplers were passed in, replace any deref of the sampler
    * with a deref of the sampler argument.
    */
   param_iter = this->actual_parameters.iterator();
   sig_param_iter = this->callee->parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_instruction *const param = (ir_instruction *) param_iter.get();
      ir_variable *sig_param = (ir_variable *) sig_param_iter.get();

      if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
	 ir_dereference *deref = param->as_dereference();

	 assert(deref);
	 do_sampler_replacement(&new_instructions, sig_param, deref);
      }
      param_iter.next();
      sig_param_iter.next();
   }

   /* Now push those new instructions in. */
   foreach_iter(exec_list_iterator, iter, new_instructions) {
      ir_instruction *ir = (ir_instruction *)iter.get();
      next_ir->insert_before(ir);
   }

   /* Copy back the value of any 'out' parameters from the function body
    * variables to our own.
    */
   i = 0;
   param_iter = this->actual_parameters.iterator();
   sig_param_iter = this->callee->parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_instruction *const param = (ir_instruction *) param_iter.get();
      const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();

      /* Move our param variable into the actual param if it's an 'out' type. */
      if (parameters[i] && (sig_param->mode == ir_var_out ||
			    sig_param->mode == ir_var_inout)) {
	 ir_assignment *assign;

	 assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
					 new(ctx) ir_dereference_variable(parameters[i]),
					 NULL);
	 next_ir->insert_before(assign);
      }

      param_iter.next();
      sig_param_iter.next();
   }

   delete [] parameters;

   hash_table_dtor(ht);

   if (retval)
      return new(ctx) ir_dereference_variable(retval);
   else
      return NULL;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_expression *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_return *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_texture *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_call *ir)
{
   if (can_inline(ir)) {
      /* If the call was part of some tree, then it should have been
       * flattened out or we shouldn't have seen it because of a
       * visit_continue_with_parent in this visitor.
       */
      assert(ir == base_ir);

      (void) ir->generate_inline(ir, this->current_function);
      ir->remove();
      this->progress = true;
   }

   return visit_continue;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
{
   ir_call *call = ir->rhs->as_call();
   if (!call || !can_inline(call))
      return visit_continue;

   /* generates the parameter setup, function body, and returns the return
    * value of the function
    */
   ir_rvalue *rhs = call->generate_inline(ir, this->current_function);
   assert(rhs);

   ir->rhs = rhs;
   this->progress = true;

   return visit_continue;
}

/**
 * Replaces references to the "sampler" variable with a clone of "deref."
 *
 * From the spec, samplers can appear in the tree as function
 * (non-out) parameters and as the result of array indexing and
 * structure field selection.  In our builtin implementation, they
 * also appear in the sampler field of an ir_tex instruction.
 */

class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
public:
   ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
   {
      this->sampler = sampler;
      this->deref = deref;
   }

   virtual ~ir_sampler_replacement_visitor()
   {
   }

   virtual ir_visitor_status visit_leave(ir_call *);
   virtual ir_visitor_status visit_leave(ir_dereference_array *);
   virtual ir_visitor_status visit_leave(ir_dereference_record *);
   virtual ir_visitor_status visit_leave(ir_texture *);

   void replace_deref(ir_dereference **deref);
   void replace_rvalue(ir_rvalue **rvalue);

   ir_variable *sampler;
   ir_dereference *deref;
};

void
ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
{
   ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
   if (deref_var && deref_var->var == this->sampler) {
      *deref = this->deref->clone(talloc_parent(*deref), NULL);
   }
}

void
ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
{
   if (!*rvalue)
      return;

   ir_dereference *deref = (*rvalue)->as_dereference();

   if (!deref)
      return;

   replace_deref(&deref);
   *rvalue = deref;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
{
   replace_deref(&ir->sampler);

   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
{
   replace_rvalue(&ir->array);
   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
{
   replace_rvalue(&ir->record);
   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
{
   foreach_iter(exec_list_iterator, iter, *ir) {
      ir_rvalue *param = (ir_rvalue *)iter.get();
      ir_rvalue *new_param = param;
      replace_rvalue(&new_param);

      if (new_param != param) {
	 param->replace_with(new_param);
      }
   }
   return visit_continue;
}

static void
do_sampler_replacement(exec_list *instructions,
		       ir_variable *sampler,
		       ir_dereference *deref)
{
   ir_sampler_replacement_visitor v(sampler, deref);

   visit_list_elements(&v, instructions);
}
<|endoftext|>


<commit_before>/* Siconos-Kernel, Copyright INRIA 2005-2011.
 * Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 * Siconos is a free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * Siconos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Siconos; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact: Vincent ACARY, siconos-team@lists.gforge.inria.fr
*/
/*! \file
  Time-Stepping simulation with projections on constraints
*/
#ifndef TIMESTEPPINGPROJECTONCONSTRAINTS_H
#define TIMESTEPPINGPROJECTONCONSTRAINTS_H

#include "TimeStepping.hpp"

/** Time-Stepping scheme
 *
 *  \author SICONOS Development Team - copyright INRIA
 *  \version 3.0.0.
 *  \date (Creation) Aug 2010
 *
 */
class TimeSteppingProjectOnConstraints : public TimeStepping
{
protected:
  /** serialization hooks
   */
  ACCEPT_SERIALIZATION(TimeSteppingProjectOnConstraints);

  virtual void initOSNS();

  /** tolerance for the violation of the equality
   *  constraints at the  position level.
   */
  double _constraintTol;

  /** tolerance for the violation of the unilateral
   *  constraints at the  position level.
   */
  double _constraintTolUnilateral;

  /** Default maximum number of Newton iteration*/
  unsigned int _projectionMaxIteration;

  /** disabled or enabled projection (Debug Projection) */
  unsigned int _doProj;
  unsigned int _doOnlyProj;

public:

  /** Constructor with the time-discretisation.
  *  \param a pointer to a timeDiscretisation (linked to the model
  *  that owns this simulation)
     \param a one step integrator
     \param a one step non smooth problem for the velocity formulation
     \param a one step non smooth problem for the position formulation
  */
  TimeSteppingProjectOnConstraints(SP::TimeDiscretisation td,
                                   SP::OneStepIntegrator osi,
                                   SP::OneStepNSProblem osnspb_velo,
                                   SP::OneStepNSProblem osnspb_pos);

  /** default constructor
   */
  TimeSteppingProjectOnConstraints() {};

  virtual ~TimeSteppingProjectOnConstraints();

  virtual void updateWorldFromDS()
  {
    ;
  }
  inline void setConstraintTol(double v)
  {
    _constraintTol = v;
  }

  inline void setProjectionMaxIteration(unsigned int v)
  {
    _projectionMaxIteration = v;
  }

  inline void setDoProj(unsigned int v)
  {
    _doProj = v;
  }
  inline void setDoOnlyProj(unsigned int v)
  {
    _doOnlyProj = v;
  }

  /** newton algorithm
   * \param double, convergence criterion
   * \param unsigned int: maximum number of Newton steps
   */
  void newtonSolve(double, unsigned int);

  /** visitors hook
   */
  //ACCEPT_STD_VISITORS();

};

DEFINE_SPTR(TimeSteppingProjectOnConstraints);

#endif // TIMESTEPPINGPROJECTONCONSTRAINTS_H

<commit_msg>add accessors<commit_after>/* Siconos-Kernel, Copyright INRIA 2005-2011.
 * Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 * Siconos is a free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * Siconos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Siconos; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact: Vincent ACARY, siconos-team@lists.gforge.inria.fr
*/
/*! \file
  Time-Stepping simulation with projections on constraints
*/
#ifndef TIMESTEPPINGPROJECTONCONSTRAINTS_H
#define TIMESTEPPINGPROJECTONCONSTRAINTS_H

#include "TimeStepping.hpp"

/** Time-Stepping scheme
 *
 *  \author SICONOS Development Team - copyright INRIA
 *  \version 3.0.0.
 *  \date (Creation) Aug 2010
 *
 */
class TimeSteppingProjectOnConstraints : public TimeStepping
{
protected:
  /** serialization hooks
   */
  ACCEPT_SERIALIZATION(TimeSteppingProjectOnConstraints);

  virtual void initOSNS();

  /** tolerance for the violation of the equality
   *  constraints at the  position level.
   */
  double _constraintTol;

  /** tolerance for the violation of the unilateral
   *  constraints at the  position level.
   */
  double _constraintTolUnilateral;

  /** Default maximum number of Newton iteration*/
  unsigned int _projectionMaxIteration;

  /** disabled or enabled projection (Debug Projection) */
  unsigned int _doProj;
  unsigned int _doOnlyProj;

public:

  /** Constructor with the time-discretisation.
  *  \param a pointer to a timeDiscretisation (linked to the model
  *  that owns this simulation)
     \param a one step integrator
     \param a one step non smooth problem for the velocity formulation
     \param a one step non smooth problem for the position formulation
  */
  TimeSteppingProjectOnConstraints(SP::TimeDiscretisation td,
                                   SP::OneStepIntegrator osi,
                                   SP::OneStepNSProblem osnspb_velo,
                                   SP::OneStepNSProblem osnspb_pos);

  /** default constructor
   */
  TimeSteppingProjectOnConstraints() {};

  virtual ~TimeSteppingProjectOnConstraints();

  virtual void updateWorldFromDS()
  {
    ;
  }

  inline void setConstraintTol(double v)
  {
    _constraintTol = v;
  }

  inline void setConstraintTolUnilateral(double v)
  {
    _constraintTolUnilateral = v;
  }

  inline void setProjectionMaxIteration(unsigned int v)
  {
    _projectionMaxIteration = v;
  }

  inline void setDoProj(unsigned int v)
  {
    _doProj = v;
  }
  inline void setDoOnlyProj(unsigned int v)
  {
    _doOnlyProj = v;
  }

  /** newton algorithm
   * \param double, convergence criterion
   * \param unsigned int: maximum number of Newton steps
   */
  void newtonSolve(double, unsigned int);

  /** visitors hook
   */
  //ACCEPT_STD_VISITORS();

};

DEFINE_SPTR(TimeSteppingProjectOnConstraints);

#endif // TIMESTEPPINGPROJECTONCONSTRAINTS_H

<|endoftext|>


<commit_before>/**************************************************************************
 *
 * Copyright 2011 Jose Fonseca
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include <string.h>
#include <iostream>

#include "os_binary.hpp"
#include "os_time.hpp"
#include "image.hpp"
#include "trace_callset.hpp"
#include "trace_dump.hpp"
#include "retrace.hpp"

static bool waitOnFinish = false;

static const char *comparePrefix = NULL;
static const char *snapshotPrefix = NULL;
static trace::CallSet snapshotFrequency;
static trace::CallSet compareFrequency;

static unsigned dumpStateCallNo = ~0;

namespace retrace {

trace::Parser parser;

int verbosity = 0;
bool debug = true;
bool profiling = false;

bool doubleBuffer = true;
bool coreProfile = false;

static unsigned frameNo = 0;

void
frameComplete(trace::Call &call) {
    ++frameNo;
}

static void
takeSnapshot(unsigned call_no) {
    assert(snapshotPrefix || comparePrefix);

    image::Image *ref = NULL;

    if (comparePrefix) {
        os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
        ref = image::readPNG(filename);
        if (!ref) {
            return;
        }
        if (retrace::verbosity >= 0) {
            std::cout << "Read " << filename << "\n";
        }
    }

    image::Image *src = getSnapshot();
    if (!src) {
        return;
    }

    if (snapshotPrefix) {
        if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
            char comment[21];
            snprintf(comment, sizeof comment, "%u", call_no);
            src->writePNM(std::cout, comment);
        } else {
            os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
            if (src->writePNG(filename) && retrace::verbosity >= 0) {
                std::cout << "Wrote " << filename << "\n";
            }
        }
    }

    if (ref) {
        std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
        delete ref;
    }

    delete src;

    return;
}

static void
mainLoop() {
    retrace::Retracer retracer;

    addCallbacks(retracer);

    long long startTime = 0;
    frameNo = 0;

    startTime = os::getTime();
    trace::Call *call;

    while ((call = retrace::parser.parse_call())) {
        bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
        bool doSnapshot =
            snapshotFrequency.contains(*call) ||
            compareFrequency.contains(*call)
        ;

        // For calls which cause rendertargets to be swaped, we take the
        // snapshot _before_ swapping the rendertargets.
        if (doSnapshot && swapRenderTarget) {
            if (call->flags & trace::CALL_FLAG_END_FRAME) {
                // For swapbuffers/presents we still use this call number,
                // spite not have been executed yet.
                takeSnapshot(call->no);
            } else {
                // Whereas for ordinate fbo/rendertarget changes we use the
                // previous call's number.
                takeSnapshot(call->no - 1);
            }
        }

        retracer.retrace(*call);

        if (doSnapshot && !swapRenderTarget) {
            takeSnapshot(call->no);
        }

        if (call->no >= dumpStateCallNo &&
            dumpState(std::cout)) {
            exit(0);
        }

        delete call;
    }

    // Reached the end of trace
    flushRendering();

    long long endTime = os::getTime();
    float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);

    if ((retrace::verbosity >= -1) || (retrace::profiling)) {
        std::cout <<
            "Rendered " << frameNo << " frames"
            " in " <<  timeInterval << " secs,"
            " average of " << (frameNo/timeInterval) << " fps\n";
    }

    if (waitOnFinish) {
        waitForInput();
    } else {
        return;
    }
}

} /* namespace retrace */

static void
usage(const char *argv0) {
    std::cout <<
        "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
        "Replay TRACE.\n"
        "\n"
        "  -b           benchmark mode (no error checking or warning messages)\n"
        "  -p           profiling mode (run whole trace, dump profiling info)\n"
        "  -c PREFIX    compare against snapshots\n"
        "  -C CALLSET   calls to compare (default is every frame)\n"
        "  -core        use core profile\n"
        "  -db          use a double buffer visual (default)\n"
        "  -sb          use a single buffer visual\n"
        "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
        "  -S CALLSET   calls to snapshot (default is every frame)\n"
        "  -v           increase output verbosity\n"
        "  -D CALLNO    dump state at specific call no\n"
        "  -w           waitOnFinish on final frame\n";
}

extern "C"
int main(int argc, char **argv)
{
    assert(compareFrequency.empty());
    assert(snapshotFrequency.empty());

    int i;
    for (i = 1; i < argc; ++i) {
        const char *arg = argv[i];

        if (arg[0] != '-') {
            break;
        }

        if (!strcmp(arg, "--")) {
            break;
        } else if (!strcmp(arg, "-b")) {
            retrace::debug = false;
            retrace::verbosity = -1;
        } else if (!strcmp(arg, "-p")) {
            retrace::debug = false;
            retrace::profiling = true;
            retrace::verbosity = -1;
        } else if (!strcmp(arg, "-c")) {
            comparePrefix = argv[++i];
            if (compareFrequency.empty()) {
                compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
            }
        } else if (!strcmp(arg, "-C")) {
            compareFrequency = trace::CallSet(argv[++i]);
            if (comparePrefix == NULL) {
                comparePrefix = "";
            }
        } else if (!strcmp(arg, "-D")) {
            dumpStateCallNo = atoi(argv[++i]);
            retrace::verbosity = -2;
        } else if (!strcmp(arg, "-core")) {
            retrace::coreProfile = true;
        } else if (!strcmp(arg, "-db")) {
            retrace::doubleBuffer = true;
        } else if (!strcmp(arg, "-sb")) {
            retrace::doubleBuffer = false;
        } else if (!strcmp(arg, "--help")) {
            usage(argv[0]);
            return 0;
        } else if (!strcmp(arg, "-s")) {
            snapshotPrefix = argv[++i];
            if (snapshotFrequency.empty()) {
                snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
            }
            if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
                os::setBinaryMode(stdout);
                retrace::verbosity = -2;
            }
        } else if (!strcmp(arg, "-S")) {
            snapshotFrequency = trace::CallSet(argv[++i]);
            if (snapshotPrefix == NULL) {
                snapshotPrefix = "";
            }
        } else if (!strcmp(arg, "-v")) {
            ++retrace::verbosity;
        } else if (!strcmp(arg, "-w")) {
            waitOnFinish = true;
        } else {
            std::cerr << "error: unknown option " << arg << "\n";
            usage(argv[0]);
            return 1;
        }
    }

    retrace::setUp();

    for ( ; i < argc; ++i) {
        if (!retrace::parser.open(argv[i])) {
            std::cerr << "error: failed to open " << argv[i] << "\n";
            return 1;
        }

        retrace::mainLoop();

        retrace::parser.close();
    }

    retrace::cleanUp();

    return 0;
}

<commit_msg>Prevent hangs in XCloseDisplay.<commit_after>/**************************************************************************
 *
 * Copyright 2011 Jose Fonseca
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include <string.h>
#include <iostream>

#include "os_binary.hpp"
#include "os_time.hpp"
#include "image.hpp"
#include "trace_callset.hpp"
#include "trace_dump.hpp"
#include "retrace.hpp"

static bool waitOnFinish = false;

static const char *comparePrefix = NULL;
static const char *snapshotPrefix = NULL;
static trace::CallSet snapshotFrequency;
static trace::CallSet compareFrequency;

static unsigned dumpStateCallNo = ~0;

namespace retrace {

trace::Parser parser;

int verbosity = 0;
bool debug = true;
bool profiling = false;

bool doubleBuffer = true;
bool coreProfile = false;

static unsigned frameNo = 0;

void
frameComplete(trace::Call &call) {
    ++frameNo;
}

static void
takeSnapshot(unsigned call_no) {
    assert(snapshotPrefix || comparePrefix);

    image::Image *ref = NULL;

    if (comparePrefix) {
        os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
        ref = image::readPNG(filename);
        if (!ref) {
            return;
        }
        if (retrace::verbosity >= 0) {
            std::cout << "Read " << filename << "\n";
        }
    }

    image::Image *src = getSnapshot();
    if (!src) {
        return;
    }

    if (snapshotPrefix) {
        if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
            char comment[21];
            snprintf(comment, sizeof comment, "%u", call_no);
            src->writePNM(std::cout, comment);
        } else {
            os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
            if (src->writePNG(filename) && retrace::verbosity >= 0) {
                std::cout << "Wrote " << filename << "\n";
            }
        }
    }

    if (ref) {
        std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
        delete ref;
    }

    delete src;

    return;
}

static void
mainLoop() {
    retrace::Retracer retracer;

    addCallbacks(retracer);

    long long startTime = 0;
    frameNo = 0;

    startTime = os::getTime();
    trace::Call *call;

    while ((call = retrace::parser.parse_call())) {
        bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
        bool doSnapshot =
            snapshotFrequency.contains(*call) ||
            compareFrequency.contains(*call)
        ;

        // For calls which cause rendertargets to be swaped, we take the
        // snapshot _before_ swapping the rendertargets.
        if (doSnapshot && swapRenderTarget) {
            if (call->flags & trace::CALL_FLAG_END_FRAME) {
                // For swapbuffers/presents we still use this call number,
                // spite not have been executed yet.
                takeSnapshot(call->no);
            } else {
                // Whereas for ordinate fbo/rendertarget changes we use the
                // previous call's number.
                takeSnapshot(call->no - 1);
            }
        }

        retracer.retrace(*call);

        if (doSnapshot && !swapRenderTarget) {
            takeSnapshot(call->no);
        }

        if (call->no >= dumpStateCallNo &&
            dumpState(std::cout)) {
            exit(0);
        }

        delete call;
    }

    // Reached the end of trace
    flushRendering();

    long long endTime = os::getTime();
    float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);

    if ((retrace::verbosity >= -1) || (retrace::profiling)) {
        std::cout <<
            "Rendered " << frameNo << " frames"
            " in " <<  timeInterval << " secs,"
            " average of " << (frameNo/timeInterval) << " fps\n";
    }

    if (waitOnFinish) {
        waitForInput();
    } else {
        return;
    }
}

} /* namespace retrace */

static void
usage(const char *argv0) {
    std::cout <<
        "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
        "Replay TRACE.\n"
        "\n"
        "  -b           benchmark mode (no error checking or warning messages)\n"
        "  -p           profiling mode (run whole trace, dump profiling info)\n"
        "  -c PREFIX    compare against snapshots\n"
        "  -C CALLSET   calls to compare (default is every frame)\n"
        "  -core        use core profile\n"
        "  -db          use a double buffer visual (default)\n"
        "  -sb          use a single buffer visual\n"
        "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
        "  -S CALLSET   calls to snapshot (default is every frame)\n"
        "  -v           increase output verbosity\n"
        "  -D CALLNO    dump state at specific call no\n"
        "  -w           waitOnFinish on final frame\n";
}

extern "C"
int main(int argc, char **argv)
{
    assert(compareFrequency.empty());
    assert(snapshotFrequency.empty());

    int i;
    for (i = 1; i < argc; ++i) {
        const char *arg = argv[i];

        if (arg[0] != '-') {
            break;
        }

        if (!strcmp(arg, "--")) {
            break;
        } else if (!strcmp(arg, "-b")) {
            retrace::debug = false;
            retrace::verbosity = -1;
        } else if (!strcmp(arg, "-p")) {
            retrace::debug = false;
            retrace::profiling = true;
            retrace::verbosity = -1;
        } else if (!strcmp(arg, "-c")) {
            comparePrefix = argv[++i];
            if (compareFrequency.empty()) {
                compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
            }
        } else if (!strcmp(arg, "-C")) {
            compareFrequency = trace::CallSet(argv[++i]);
            if (comparePrefix == NULL) {
                comparePrefix = "";
            }
        } else if (!strcmp(arg, "-D")) {
            dumpStateCallNo = atoi(argv[++i]);
            retrace::verbosity = -2;
        } else if (!strcmp(arg, "-core")) {
            retrace::coreProfile = true;
        } else if (!strcmp(arg, "-db")) {
            retrace::doubleBuffer = true;
        } else if (!strcmp(arg, "-sb")) {
            retrace::doubleBuffer = false;
        } else if (!strcmp(arg, "--help")) {
            usage(argv[0]);
            return 0;
        } else if (!strcmp(arg, "-s")) {
            snapshotPrefix = argv[++i];
            if (snapshotFrequency.empty()) {
                snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
            }
            if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
                os::setBinaryMode(stdout);
                retrace::verbosity = -2;
            }
        } else if (!strcmp(arg, "-S")) {
            snapshotFrequency = trace::CallSet(argv[++i]);
            if (snapshotPrefix == NULL) {
                snapshotPrefix = "";
            }
        } else if (!strcmp(arg, "-v")) {
            ++retrace::verbosity;
        } else if (!strcmp(arg, "-w")) {
            waitOnFinish = true;
        } else {
            std::cerr << "error: unknown option " << arg << "\n";
            usage(argv[0]);
            return 1;
        }
    }

    retrace::setUp();

    for ( ; i < argc; ++i) {
        if (!retrace::parser.open(argv[i])) {
            std::cerr << "error: failed to open " << argv[i] << "\n";
            return 1;
        }

        retrace::mainLoop();

        retrace::parser.close();
    }

    // XXX: X often hangs on XCloseDisplay
    //retrace::cleanUp();

    return 0;
}

<|endoftext|>