<commit_before>
#include "content/web_impl_win/WebFileUtilitiesImpl.h"

#include "third_party/WebKit/public/platform/WebFileInfo.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/Source/platform/weborigin/KURL.h"
#include "third_party/WebKit/Source/wtf/text/WTFStringUtil.h"
#include "third_party/WebKit/Source/wtf/CryptographicallyRandomNumber.h"

#define PURE = 0

#include <windows.h>
#include <Shlwapi.h>

namespace content {

static const ULONGLONG kSecondsFromFileTimeToTimet = 11644473600;

static bool getFindData(String path, WIN32_FIND_DATAW& findData)
{
    Vector<UChar> upath = WTF::ensureUTF16UChar(path, true);
    HANDLE handle = ::FindFirstFileW(upath.data(), &findData);
    if (handle == INVALID_HANDLE_VALUE)
        return false;
    ::FindClose(handle);
    return true;
}

String pathByAppendingComponent(const String& path, const String& component)
{
    Vector<UChar> buffer(MAX_PATH);

    if (path.length() + 1 > buffer.size())
        return String();

    //StringView(path).getCharactersWithUpconvert(buffer.data());
    Vector<UChar> path16 = WTF::ensureUTF16UChar(path, true);
    for (size_t i = 0; i < path.length(); ++i) {
        buffer[i] = path16[i];
    }
    //buffer[path.length()] = '\0';

    if (!PathAppendW(buffer.data(), component.charactersWithNullTermination().data()))
        return String();

    buffer.shrink(wcslen(buffer.data()));

    return String(buffer.data(), buffer.size());
}

String openTemporaryFile(const String&, HANDLE& handle)
{
    handle = INVALID_HANDLE_VALUE;

    wchar_t tempPath[MAX_PATH];
    int tempPathLength = ::GetTempPathW(WTF_ARRAY_LENGTH(tempPath), tempPath);
    if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath))
        return String();

    String proposedPath;
    do {
        wchar_t tempFile[] = L"XXXXXXXX.tmp"; // Use 8.3 style name (more characters aren't helpful due to 8.3 short file names)
        const int randomPartLength = 8;
        WTF::cryptographicallyRandomValues(tempFile, randomPartLength * sizeof(wchar_t));

        // Limit to valid filesystem characters, also excluding others that could be problematic, like punctuation.
        // don't include both upper and lowercase since Windows file systems are typically not case sensitive.
        const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
        for (int i = 0; i < randomPartLength; ++i)
            tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)];

        ASSERT(wcslen(tempFile) == WTF_ARRAY_LENGTH(tempFile) - 1);

        proposedPath = pathByAppendingComponent(tempPath, tempFile);
        if (proposedPath.isEmpty())
            break;

        // use CREATE_NEW to avoid overwriting an existing file with the same name
        handle = ::CreateFileW(proposedPath.charactersWithNullTermination().data(), GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
    } while (!(INVALID_HANDLE_VALUE == handle) && GetLastError() == ERROR_ALREADY_EXISTS);

    if (!(INVALID_HANDLE_VALUE != handle))
        return String();

    return proposedPath;
}

int writeToFile(HANDLE handle, const char* data, int length)
{
    if (!(INVALID_HANDLE_VALUE != handle))
        return -1;

    DWORD bytesWritten;
    bool success = ::WriteFile(handle, data, length, &bytesWritten, 0);

    if (!success)
        return -1;
    return static_cast<int>(bytesWritten);
}

String pathGetFileName(const String& path)
{
    return String(::PathFindFileNameW(WTF::ensureUTF16UChar(path, true).data()));
}

bool fileExists(const String& path)
{
    WIN32_FIND_DATAW findData;
    return getFindData(path, findData);
}

static void getFileModificationTimeFromFindData(const WIN32_FIND_DATAW& findData, time_t& time)
{
    ULARGE_INTEGER fileTime;
    fileTime.HighPart = findData.ftLastWriteTime.dwHighDateTime;
    fileTime.LowPart = findData.ftLastWriteTime.dwLowDateTime;

    // Information about converting time_t to FileTime is available at http://msdn.microsoft.com/en-us/library/ms724228%28v=vs.85%29.aspx
    time = fileTime.QuadPart / 10000000 - kSecondsFromFileTimeToTimet;
}

static bool getFileSizeFromFindData(const WIN32_FIND_DATAW& findData, long long& size)
{
    ULARGE_INTEGER fileSize;
    fileSize.HighPart = findData.nFileSizeHigh;
    fileSize.LowPart = findData.nFileSizeLow;

    if (fileSize.QuadPart > static_cast<ULONGLONG>(std::numeric_limits<long long>::max()))
        return false;

    size = fileSize.QuadPart;
    return true;
}

WebFileUtilitiesImpl::WebFileUtilitiesImpl()
{

}

bool WebFileUtilitiesImpl::getFileInfo(const blink::WebString& path, blink::WebFileInfo& result)
{
    WIN32_FIND_DATAW findData;
    if (!getFindData(path, findData))
        return false;

    time_t time;
    getFileModificationTimeFromFindData(findData, time);
    result.modificationTime = (double)time;

    getFileSizeFromFindData(findData, result.length);

    result.type = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? blink::WebFileInfo::TypeDirectory : blink::WebFileInfo::TypeFile;
    result.platformPath = path;

    return true;
}

blink::WebString WebFileUtilitiesImpl::directoryName(const blink::WebString& path)
{
    String pathString(path);
    String name = pathString.left(pathString.length() - pathGetFileName(pathString).length());
    if (name.characterStartingAt(name.length() - 1) == '\\') {
        // Remove any trailing "\".
        name.truncate(name.length() - 1);
    }
    return name;
}

blink::WebString WebFileUtilitiesImpl::baseName(const blink::WebString& path)
{
    if (path.isNull() || path.isEmpty())
        return "";
    Vector<UChar> result = WTF::ensureUTF16UChar(path, true);
    if (result.isEmpty())
        return "";
    ::PathStripPathW(result.data());
    return blink::WebString(result.data(), result.size() - 1);
}

bool WebFileUtilitiesImpl::isDirectory(const blink::WebString& path)
{
    return ::PathIsDirectoryW(WTF::ensureUTF16UChar(path, true).data());
}

blink::WebURL WebFileUtilitiesImpl::filePathToURL(const blink::WebString& path)
{
    blink::KURL result;
    String temp = "file:///";
    temp.append(WTF::ensureUTF16String(path));
    temp.replace("%", "%25");
    temp.replace(";", "%3B");
    temp.replace("#", "%23");
    temp.replace("?", "%3F");
    return blink::KURL(blink::ParsedURLString, temp);
}

}<commit_msg>* 修复  <input id="localfile-lexicon" style="width:100%;" type="file" />选择文件后有多余无意义字符串的bug， 原因还是上次的bug一样的原因：没处理好WebFileUtilitiesImpl::baseName的返回值，PathStripPathW出来的字符串是0结尾的，不能用result.size来获取长度<commit_after>
#include "content/web_impl_win/WebFileUtilitiesImpl.h"

#include "third_party/WebKit/public/platform/WebFileInfo.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/Source/platform/weborigin/KURL.h"
#include "third_party/WebKit/Source/wtf/text/WTFStringUtil.h"
#include "third_party/WebKit/Source/wtf/CryptographicallyRandomNumber.h"

#define PURE = 0

#include <windows.h>
#include <Shlwapi.h>

namespace content {

static const ULONGLONG kSecondsFromFileTimeToTimet = 11644473600;

static bool getFindData(String path, WIN32_FIND_DATAW& findData)
{
    Vector<UChar> upath = WTF::ensureUTF16UChar(path, true);
    HANDLE handle = ::FindFirstFileW(upath.data(), &findData);
    if (handle == INVALID_HANDLE_VALUE)
        return false;
    ::FindClose(handle);
    return true;
}

String pathByAppendingComponent(const String& path, const String& component)
{
    Vector<UChar> buffer(MAX_PATH);

    if (path.length() + 1 > buffer.size())
        return String();

    //StringView(path).getCharactersWithUpconvert(buffer.data());
    Vector<UChar> path16 = WTF::ensureUTF16UChar(path, true);
    for (size_t i = 0; i < path.length(); ++i) {
        buffer[i] = path16[i];
    }
    //buffer[path.length()] = '\0';

    if (!PathAppendW(buffer.data(), component.charactersWithNullTermination().data()))
        return String();

    buffer.shrink(wcslen(buffer.data()));

    return String(buffer.data(), buffer.size());
}

String openTemporaryFile(const String&, HANDLE& handle)
{
    handle = INVALID_HANDLE_VALUE;

    wchar_t tempPath[MAX_PATH];
    int tempPathLength = ::GetTempPathW(WTF_ARRAY_LENGTH(tempPath), tempPath);
    if (tempPathLength <= 0 || tempPathLength > WTF_ARRAY_LENGTH(tempPath))
        return String();

    String proposedPath;
    do {
        wchar_t tempFile[] = L"XXXXXXXX.tmp"; // Use 8.3 style name (more characters aren't helpful due to 8.3 short file names)
        const int randomPartLength = 8;
        WTF::cryptographicallyRandomValues(tempFile, randomPartLength * sizeof(wchar_t));

        // Limit to valid filesystem characters, also excluding others that could be problematic, like punctuation.
        // don't include both upper and lowercase since Windows file systems are typically not case sensitive.
        const char validChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
        for (int i = 0; i < randomPartLength; ++i)
            tempFile[i] = validChars[tempFile[i] % (sizeof(validChars) - 1)];

        ASSERT(wcslen(tempFile) == WTF_ARRAY_LENGTH(tempFile) - 1);

        proposedPath = pathByAppendingComponent(tempPath, tempFile);
        if (proposedPath.isEmpty())
            break;

        // use CREATE_NEW to avoid overwriting an existing file with the same name
        handle = ::CreateFileW(proposedPath.charactersWithNullTermination().data(), GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
    } while (!(INVALID_HANDLE_VALUE == handle) && GetLastError() == ERROR_ALREADY_EXISTS);

    if (!(INVALID_HANDLE_VALUE != handle))
        return String();

    return proposedPath;
}

int writeToFile(HANDLE handle, const char* data, int length)
{
    if (!(INVALID_HANDLE_VALUE != handle))
        return -1;

    DWORD bytesWritten;
    bool success = ::WriteFile(handle, data, length, &bytesWritten, 0);

    if (!success)
        return -1;
    return static_cast<int>(bytesWritten);
}

String pathGetFileName(const String& path)
{
    return String(::PathFindFileNameW(WTF::ensureUTF16UChar(path, true).data()));
}

bool fileExists(const String& path)
{
    WIN32_FIND_DATAW findData;
    return getFindData(path, findData);
}

static void getFileModificationTimeFromFindData(const WIN32_FIND_DATAW& findData, time_t& time)
{
    ULARGE_INTEGER fileTime;
    fileTime.HighPart = findData.ftLastWriteTime.dwHighDateTime;
    fileTime.LowPart = findData.ftLastWriteTime.dwLowDateTime;

    // Information about converting time_t to FileTime is available at http://msdn.microsoft.com/en-us/library/ms724228%28v=vs.85%29.aspx
    time = fileTime.QuadPart / 10000000 - kSecondsFromFileTimeToTimet;
}

static bool getFileSizeFromFindData(const WIN32_FIND_DATAW& findData, long long& size)
{
    ULARGE_INTEGER fileSize;
    fileSize.HighPart = findData.nFileSizeHigh;
    fileSize.LowPart = findData.nFileSizeLow;

    if (fileSize.QuadPart > static_cast<ULONGLONG>(std::numeric_limits<long long>::max()))
        return false;

    size = fileSize.QuadPart;
    return true;
}

WebFileUtilitiesImpl::WebFileUtilitiesImpl()
{

}

bool WebFileUtilitiesImpl::getFileInfo(const blink::WebString& path, blink::WebFileInfo& result)
{
    WIN32_FIND_DATAW findData;
    if (!getFindData(path, findData))
        return false;

    time_t time;
    getFileModificationTimeFromFindData(findData, time);
    result.modificationTime = (double)time;

    getFileSizeFromFindData(findData, result.length);

    result.type = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? blink::WebFileInfo::TypeDirectory : blink::WebFileInfo::TypeFile;
    result.platformPath = path;

    return true;
}

blink::WebString WebFileUtilitiesImpl::directoryName(const blink::WebString& path)
{
    String pathString(path);
    String name = pathString.left(pathString.length() - pathGetFileName(pathString).length());
    if (name.characterStartingAt(name.length() - 1) == '\\') {
        // Remove any trailing "\".
        name.truncate(name.length() - 1);
    }
    return name;
}

blink::WebString WebFileUtilitiesImpl::baseName(const blink::WebString& path)
{
    if (path.isNull() || path.isEmpty())
        return "";
    Vector<UChar> result = WTF::ensureUTF16UChar(path, true);
    if (result.isEmpty())
        return "";
    ::PathStripPathW(result.data());
    return String(result.data());
}

bool WebFileUtilitiesImpl::isDirectory(const blink::WebString& path)
{
    return ::PathIsDirectoryW(WTF::ensureUTF16UChar(path, true).data());
}

blink::WebURL WebFileUtilitiesImpl::filePathToURL(const blink::WebString& path)
{
    blink::KURL result;
    String temp = "file:///";
    temp.append(WTF::ensureUTF16String(path));
    temp.replace("%", "%25");
    temp.replace(";", "%3B");
    temp.replace("#", "%23");
    temp.replace("?", "%3F");
    return blink::KURL(blink::ParsedURLString, temp);
}

}<|endoftext|>


<commit_before>/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once
#include <eosiolib/eosio.hpp>
#include <eosiolib/token.hpp>
#include <eosiolib/print.hpp>

#include <eosiolib/generic_currency.hpp>
#include <eosiolib/datastream.hpp>
#include <eosiolib/serialize.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/transaction.hpp>

#include <map>

namespace eosiosystem {
   using eosio::indexed_by;
   using eosio::const_mem_fun;
   using eosio::member;
   using eosio::bytes;
   using eosio::print;
   using eosio::transaction;
   using std::map;
   using std::pair;

   template<account_name SystemAccount>
   class producers_election {
      public:
         static const account_name system_account = SystemAccount;
         typedef eosio::generic_currency< eosio::token<system_account,S(4,EOS)> > currency;
         typedef typename currency::token_type system_token_type;

         static constexpr uint32_t max_unstake_requests = 10;
         static constexpr uint32_t voting_stake_freez_time = 180*24*3600;

         struct producer_preferences {
            uint32_t max_blk_size;
            uint32_t target_blk_size;

            uint64_t max_storage_size;
            uint64_t rescource_window_size;

            uint32_t max_blk_cpu;
            uint32_t target_blk_cpu;

            uint16_t inflation_rate; // inflation in percents * 10000;

            uint32_t max_trx_lifetime;
            uint16_t max_transaction_recursion;

            producer_preferences() { bzero(this, sizeof(*this)); }

            EOSLIB_SERIALIZE( producer_preferences, (max_blk_size)(target_blk_size)(max_storage_size)(rescource_window_size)
                              (max_blk_cpu)(target_blk_cpu)(inflation_rate)(max_trx_lifetime)(max_transaction_recursion) )
         };

         struct producer_info {
            account_name      owner;
            uint64_t          padding = 0;
            uint128_t         total_votes;
            producer_preferences prefs;

            uint64_t    primary_key()const { return owner;       }
            uint128_t   by_votes()const    { return total_votes; }

            EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(prefs) )
         };

         typedef eosio::multi_index< N(producervote), producer_info,
                                     indexed_by<N(prototalvote), const_mem_fun<producer_info, uint128_t, &producer_info::by_votes>  >
                                     >  producer_info_index_type;

         struct account_votes {
            account_name                owner;
            account_name                proxy;
            uint32_t                    last_update;
            uint32_t                    i_am_proxy;
            uint128_t                   proxied_votes;
            system_token_type           staked;
            std::vector<account_name>   producers;

            uint64_t primary_key()const { return owner; }

            EOSLIB_SERIALIZE( account_votes, (owner)(proxy)(last_update)(i_am_proxy)(staked)(producers) )
         };
         typedef eosio::multi_index< N(accountvotes), account_votes>  account_votes_index_type;

         struct producer_config {
            account_name      owner;
            eosio::bytes      packed_key; /// a packed public key object

            uint64_t primary_key()const { return owner;       }
            EOSLIB_SERIALIZE( producer_config, (owner)(packed_key) )
         };
         typedef eosio::multi_index< N(producercfg), producer_config>  producer_config_index_type;

         struct unstake_request {
            uint64_t id;
            account_name account;
            system_token_type amount;
            time refund_time;

            uint64_t primary_key() const { return id; }
            uint64_t rt() const { return refund_time; }
            EOSLIB_SERIALIZE( unstake_request, (id)(account)(amount)(refund_time) )
         };

         typedef eosio::multi_index< N(unstakerequests), unstake_request,
                                     indexed_by<N(bytime), const_mem_fun<unstake_request, uint64_t, &unstake_request::rt> >
                                     > unstake_requests_table;

         struct unstake_requests_count {
            account_name account;
            uint16_t count;
            uint64_t primary_key() const { return account; }
            EOSLIB_SERIALIZE( unstake_requests_count, (account)(count) )
         };

         typedef eosio::multi_index< N(unstakecount), unstake_requests_count> unstake_requests_counts_table;

         ACTION( SystemAccount, register_producer ) {
            account_name producer;
            bytes        producer_key;
            producer_preferences prefs;

            EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) )
         };

         /**
          *  This method will create a producer_config and producer_info object for 'producer'
          *
          *  @pre producer is not already registered
          *  @pre producer to register is an account
          *  @pre authority of producer to register
          *
          */
         static void on( const register_producer& reg ) {
            require_auth( reg.producer );

            producer_info_index_type producers( SystemAccount, SystemAccount );
            const auto* existing = producers.find( reg.producer );
            eosio_assert( !existing, "producer already registered" );

            producers.emplace( reg.producer, [&]( producer_info& info ){
                  info.owner       = reg.producer;
                  info.total_votes = 0;
                  info.prefs = reg.prefs;
               });

            producer_config_index_type proconfig( SystemAccount, SystemAccount );
            proconfig.emplace( reg.producer, [&]( auto& pc ) {
                  pc.owner      = reg.producer;
                  pc.packed_key = reg.producer_key;
               });
         }

         ACTION( SystemAccount, change_producer_preferences ) {
            account_name producer;
            bytes        producer_key;
            producer_preferences prefs;

            EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) )
         };

         static void on( const change_producer_preferences& change) {
            require_auth( change.producer );

            producer_info_index_type producers( SystemAccount, SystemAccount );
            const auto* ptr = producers.find( change.producer );
            eosio_assert( bool(ptr), "producer is not registered" );

            producers.update( *ptr, change.producer, [&]( producer_info& info ){
                  info.prefs = change.prefs;
               });
         }

         ACTION( SystemAccount, stake_vote ) {
            account_name      voter;
            system_token_type amount;

            EOSLIB_SERIALIZE( stake_vote, (voter)(amount) )
         };

         static void on( const stake_vote& sv ) {
            eosio_assert( sv.amount.quantity > 0, "must stake some tokens" );
            require_auth( sv.voter );

            account_votes_index_type avotes( SystemAccount, SystemAccount );

            const auto* acv = avotes.find( sv.voter );
            if( !acv ) {
               acv = &avotes.emplace( sv.voter, [&]( account_votes& a ) {
                     a.owner = sv.voter;
                     a.last_update = now();
                     a.proxy = 0;
                     a.i_am_proxy = 0;
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }

            producer_info_index_type producers( SystemAccount, SystemAccount );

            for( auto p : acv->producers ) {
               producers.update( producers.get( p ), 0, [&]( auto& v ) {
                     v.total_votes += sv.amount.quantity;
                  });
            }

            avotes.update( *acv, 0, [&]( auto& av ) {
                  av.last_update = now();
                  av.staked += sv.amount;
               });

            currency::inline_transfer( sv.voter, SystemAccount, sv.amount, "stake for voting" );
         }

         ACTION( SystemAccount, unstake_vote ) {
            account_name      voter;
            system_token_type amount;

            EOSLIB_SERIALIZE( unstake_vote, (voter)(amount) )
         };

         static void on( const unstake_vote& usv ) {
            eosio_assert( usv.amount.quantity > 0, "unstake amount should be > 0" );
            require_auth( usv.voter );

            unstake_requests_counts_table counts( SystemAccount, SystemAccount );
            auto ptr = counts.find( usv.voter );
            eosio_assert( !ptr || ptr->count < max_unstake_requests, "unstake requests limit exceeded");

            if ( ptr ) {
               counts.update(*ptr, usv.voter, [&](auto& r) { ++r.count; });
            } else {
               counts.emplace(usv.voter, [&](auto& r) { r.count = 1; });
            }

            unstake_requests_table requests( SystemAccount, SystemAccount );
            auto pk = requests.available_primary_key();
            requests.emplace( usv.voter, [&](unstake_request& r) {
                  r.id = pk;
                  r.account = usv.voter;
                  r.refund_time = now() + voting_stake_freez_time;
               });

            account_votes_index_type avotes( SystemAccount, SystemAccount );

            const auto* acv = avotes.find( usv.voter );
            eosio_assert( bool(acv), "stake not found" );

            eosio_assert( acv->staked.quantity < usv.amount.quantity, "attempt to unstake more than total stake amount" );

            producer_info_index_type producers( SystemAccount, SystemAccount );

            for( auto p : acv->producers ) {
               producers.update( producers.get( p ), 0, [&]( auto& v ) {
                     v.total_votes -= usv.amount.quantity;
                  });
            }

            if ( usv.amount < acv->staked ) {
               avotes.update( *acv, 0, [&]( auto& av ) {
                     av.last_update = now();
                     av.staked -= usv.amount;
                  });
            } else {
               eosio_assert( usv.amount == acv->staked, "unstaking more than is at staked" );
               avotes.remove( *acv );
            }
         }

         ACTION( SystemAccount, vote_producer ) {
            account_name                voter;
            account_name                proxy;
            std::vector<account_name>   producers;

            EOSLIB_SERIALIZE( vote_producer, (voter)(proxy)(producers) )
         };

         /**
          *  @pre vp.producers must be sorted from lowest to highest
          *  @pre if proxy is set then no producers can be voted for
          *  @pre every listed producer or proxy must have been previously registered
          *  @pre vp.voter must authorize this action
          *  @pre voter must have previously staked some EOS for voting
          */
         static void on( const vote_producer& vp ) {
            require_auth( vp.voter );

            //validate input
            if ( vp.proxy ) {
               eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" );
            } else {
               eosio_assert( vp.producers.size() <= 30, "attempt to vote for too many producers" );
               eosio_assert( std::is_sorted( vp.producers.begin(), vp.producers.end() ), "producer votes must be sorted" );
            }

            account_votes_index_type avotes( SystemAccount, SystemAccount );
            auto ptr = avotes.find( vp.voter );

            eosio_assert( bool(ptr), "no stake to vote" );
            if ( ptr->i_am_proxy ) {
               eosio_assert( vp.proxy == 0 , "accounts elected to be proxy are not allowed to use another proxy" );
            }

            //find old producers, update old proxy if needed
            const std::vector<account_name>* old_producers = nullptr;
            if( ptr->proxy ) {
               if ( ptr->proxy == vp.proxy ) {
                  return; // nothing changed
               }
               auto old_proxy = avotes.find( ptr->proxy );
               avotes.update( *old_proxy, 0, [&](auto& a) { a.proxied_votes -= ptr->staked.quantity; } );
               old_producers = &old_proxy->producers;
            } else {
               old_producers = &ptr->producers;
            }

            //find new producers, update new proxy if needed
            const std::vector<account_name>* new_producers = nullptr;
            if ( vp.proxy ) {
               auto new_proxy = avotes.find( ptr->proxy );
               avotes.update( *new_proxy, 0, [&](auto& a) { a.proxied_votes += ptr->staked.quantity; } );
               new_producers = &new_proxy->producers;
            } else {
               new_producers = &vp.producers;
            }

            producer_info_index_type producers( SystemAccount, SystemAccount );

            //revoke votes only from no longer elected
            std::vector<account_name> revoked( old_producers->size() );
            auto end_it = std::set_difference( old_producers->begin(), old_producers->end(), new_producers->begin(), new_producers->end(), revoked.begin() );
            for ( auto it = revoked.begin(); it != end_it; ++it ) {
               producers.update( producers.get( *it ), 0, [&]( auto& pi ) { pi.total_votes -= ptr->staked.quantity; });
            }

            //update newly elected
            std::vector<account_name> elected( new_producers->size() );
            end_it = std::set_difference( new_producers->begin(), new_producers->end(), old_producers->begin(), old_producers->end(), elected.begin() );
            for ( auto it = elected.begin(); it != end_it; ++it ) {
               producers.update( producers.get( *it ), 0, [&]( auto& pi ) { pi.total_votes += ptr->staked.quantity; });
            }

            // save new values to the account itself
            avotes.update( *ptr, 0, [&](account_votes& a) {
                  a.proxy = vp.proxy;
                  a.last_update = now();
                  a.producers = vp.producers;
               });
         }

         ACTION( SystemAccount, register_proxy ) {
            account_name proxy_to_register;

            EOSLIB_SERIALIZE( register_proxy, (proxy_to_register) )
         };

         static void on( const register_proxy& reg ) {
            require_auth( reg.proxy_to_register );

            account_votes_index_type avotes( SystemAccount, SystemAccount );
            auto ptr = avotes.find( reg.proxy_to_register );
            if ( ptr ) {
               eosio_assert( ptr->i_am_proxy == 0, "account is already a proxy" );
               eosio_assert( ptr->proxy == 0, "account that uses a proxy is not allowed to become a proxy" );
               avotes.update( *ptr, 0, [&](account_votes& a) {
                     a.i_am_proxy = 1;
                     a.last_update = now();
                  });
            } else {
               avotes.emplace( reg.proxy_to_register, [&]( account_votes& a ) {
                     a.owner = reg.proxy_to_register;
                     a.last_update = now();
                     a.proxy = 0;
                     a.i_am_proxy = 1;
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }
         }

         struct block {};

         static void on( const block& ) {
         }
   };
}
<commit_msg>table name fix #1455<commit_after>/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once
#include <eosiolib/eosio.hpp>
#include <eosiolib/token.hpp>
#include <eosiolib/print.hpp>

#include <eosiolib/generic_currency.hpp>
#include <eosiolib/datastream.hpp>
#include <eosiolib/serialize.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/transaction.hpp>

#include <map>

namespace eosiosystem {
   using eosio::indexed_by;
   using eosio::const_mem_fun;
   using eosio::member;
   using eosio::bytes;
   using eosio::print;
   using eosio::transaction;
   using std::map;
   using std::pair;

   template<account_name SystemAccount>
   class producers_election {
      public:
         static const account_name system_account = SystemAccount;
         typedef eosio::generic_currency< eosio::token<system_account,S(4,EOS)> > currency;
         typedef typename currency::token_type system_token_type;

         static constexpr uint32_t max_unstake_requests = 10;
         static constexpr uint32_t voting_stake_freez_time = 180*24*3600;

         struct producer_preferences {
            uint32_t max_blk_size;
            uint32_t target_blk_size;

            uint64_t max_storage_size;
            uint64_t rescource_window_size;

            uint32_t max_blk_cpu;
            uint32_t target_blk_cpu;

            uint16_t inflation_rate; // inflation in percents * 10000;

            uint32_t max_trx_lifetime;
            uint16_t max_transaction_recursion;

            producer_preferences() { bzero(this, sizeof(*this)); }

            EOSLIB_SERIALIZE( producer_preferences, (max_blk_size)(target_blk_size)(max_storage_size)(rescource_window_size)
                              (max_blk_cpu)(target_blk_cpu)(inflation_rate)(max_trx_lifetime)(max_transaction_recursion) )
         };

         struct producer_info {
            account_name      owner;
            uint64_t          padding = 0;
            uint128_t         total_votes;
            producer_preferences prefs;

            uint64_t    primary_key()const { return owner;       }
            uint128_t   by_votes()const    { return total_votes; }

            EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(prefs) )
         };

         typedef eosio::multi_index< N(producervote), producer_info,
                                     indexed_by<N(prototalvote), const_mem_fun<producer_info, uint128_t, &producer_info::by_votes>  >
                                     >  producer_info_index_type;

         struct account_votes {
            account_name                owner;
            account_name                proxy;
            uint32_t                    last_update;
            uint32_t                    i_am_proxy;
            uint128_t                   proxied_votes;
            system_token_type           staked;
            std::vector<account_name>   producers;

            uint64_t primary_key()const { return owner; }

            EOSLIB_SERIALIZE( account_votes, (owner)(proxy)(last_update)(i_am_proxy)(staked)(producers) )
         };
         typedef eosio::multi_index< N(accountvotes), account_votes>  account_votes_index_type;

         struct producer_config {
            account_name      owner;
            eosio::bytes      packed_key; /// a packed public key object

            uint64_t primary_key()const { return owner;       }
            EOSLIB_SERIALIZE( producer_config, (owner)(packed_key) )
         };
         typedef eosio::multi_index< N(producercfg), producer_config>  producer_config_index_type;

         struct unstake_request {
            uint64_t id;
            account_name account;
            system_token_type amount;
            time refund_time;

            uint64_t primary_key() const { return id; }
            uint64_t rt() const { return refund_time; }
            EOSLIB_SERIALIZE( unstake_request, (id)(account)(amount)(refund_time) )
         };

         typedef eosio::multi_index< N(unstakereqs), unstake_request,
                                     indexed_by<N(bytime), const_mem_fun<unstake_request, uint64_t, &unstake_request::rt> >
                                     > unstake_requests_table;

         struct unstake_requests_count {
            account_name account;
            uint16_t count;
            uint64_t primary_key() const { return account; }
            EOSLIB_SERIALIZE( unstake_requests_count, (account)(count) )
         };

         typedef eosio::multi_index< N(unstakecount), unstake_requests_count> unstake_requests_counts_table;

         ACTION( SystemAccount, register_producer ) {
            account_name producer;
            bytes        producer_key;
            producer_preferences prefs;

            EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) )
         };

         /**
          *  This method will create a producer_config and producer_info object for 'producer'
          *
          *  @pre producer is not already registered
          *  @pre producer to register is an account
          *  @pre authority of producer to register
          *
          */
         static void on( const register_producer& reg ) {
            require_auth( reg.producer );

            producer_info_index_type producers( SystemAccount, SystemAccount );
            const auto* existing = producers.find( reg.producer );
            eosio_assert( !existing, "producer already registered" );

            producers.emplace( reg.producer, [&]( producer_info& info ){
                  info.owner       = reg.producer;
                  info.total_votes = 0;
                  info.prefs = reg.prefs;
               });

            producer_config_index_type proconfig( SystemAccount, SystemAccount );
            proconfig.emplace( reg.producer, [&]( auto& pc ) {
                  pc.owner      = reg.producer;
                  pc.packed_key = reg.producer_key;
               });
         }

         ACTION( SystemAccount, change_producer_preferences ) {
            account_name producer;
            bytes        producer_key;
            producer_preferences prefs;

            EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) )
         };

         static void on( const change_producer_preferences& change) {
            require_auth( change.producer );

            producer_info_index_type producers( SystemAccount, SystemAccount );
            const auto* ptr = producers.find( change.producer );
            eosio_assert( bool(ptr), "producer is not registered" );

            producers.update( *ptr, change.producer, [&]( producer_info& info ){
                  info.prefs = change.prefs;
               });
         }

         ACTION( SystemAccount, stake_vote ) {
            account_name      voter;
            system_token_type amount;

            EOSLIB_SERIALIZE( stake_vote, (voter)(amount) )
         };

         static void on( const stake_vote& sv ) {
            eosio_assert( sv.amount.quantity > 0, "must stake some tokens" );
            require_auth( sv.voter );

            account_votes_index_type avotes( SystemAccount, SystemAccount );

            const auto* acv = avotes.find( sv.voter );
            if( !acv ) {
               acv = &avotes.emplace( sv.voter, [&]( account_votes& a ) {
                     a.owner = sv.voter;
                     a.last_update = now();
                     a.proxy = 0;
                     a.i_am_proxy = 0;
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }

            producer_info_index_type producers( SystemAccount, SystemAccount );

            for( auto p : acv->producers ) {
               producers.update( producers.get( p ), 0, [&]( auto& v ) {
                     v.total_votes += sv.amount.quantity;
                  });
            }

            avotes.update( *acv, 0, [&]( auto& av ) {
                  av.last_update = now();
                  av.staked += sv.amount;
               });

            currency::inline_transfer( sv.voter, SystemAccount, sv.amount, "stake for voting" );
         }

         ACTION( SystemAccount, unstake_vote ) {
            account_name      voter;
            system_token_type amount;

            EOSLIB_SERIALIZE( unstake_vote, (voter)(amount) )
         };

         static void on( const unstake_vote& usv ) {
            eosio_assert( usv.amount.quantity > 0, "unstake amount should be > 0" );
            require_auth( usv.voter );

            unstake_requests_counts_table counts( SystemAccount, SystemAccount );
            auto ptr = counts.find( usv.voter );
            eosio_assert( !ptr || ptr->count < max_unstake_requests, "unstake requests limit exceeded");

            if ( ptr ) {
               counts.update(*ptr, usv.voter, [&](auto& r) { ++r.count; });
            } else {
               counts.emplace(usv.voter, [&](auto& r) { r.count = 1; });
            }

            unstake_requests_table requests( SystemAccount, SystemAccount );
            auto pk = requests.available_primary_key();
            requests.emplace( usv.voter, [&](unstake_request& r) {
                  r.id = pk;
                  r.account = usv.voter;
                  r.refund_time = now() + voting_stake_freez_time;
               });

            account_votes_index_type avotes( SystemAccount, SystemAccount );

            const auto* acv = avotes.find( usv.voter );
            eosio_assert( bool(acv), "stake not found" );

            eosio_assert( acv->staked.quantity < usv.amount.quantity, "attempt to unstake more than total stake amount" );

            producer_info_index_type producers( SystemAccount, SystemAccount );

            for( auto p : acv->producers ) {
               producers.update( producers.get( p ), 0, [&]( auto& v ) {
                     v.total_votes -= usv.amount.quantity;
                  });
            }

            if ( usv.amount < acv->staked ) {
               avotes.update( *acv, 0, [&]( auto& av ) {
                     av.last_update = now();
                     av.staked -= usv.amount;
                  });
            } else {
               eosio_assert( usv.amount == acv->staked, "unstaking more than is at staked" );
               avotes.remove( *acv );
            }
         }

         ACTION( SystemAccount, vote_producer ) {
            account_name                voter;
            account_name                proxy;
            std::vector<account_name>   producers;

            EOSLIB_SERIALIZE( vote_producer, (voter)(proxy)(producers) )
         };

         /**
          *  @pre vp.producers must be sorted from lowest to highest
          *  @pre if proxy is set then no producers can be voted for
          *  @pre every listed producer or proxy must have been previously registered
          *  @pre vp.voter must authorize this action
          *  @pre voter must have previously staked some EOS for voting
          */
         static void on( const vote_producer& vp ) {
            require_auth( vp.voter );

            //validate input
            if ( vp.proxy ) {
               eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" );
            } else {
               eosio_assert( vp.producers.size() <= 30, "attempt to vote for too many producers" );
               eosio_assert( std::is_sorted( vp.producers.begin(), vp.producers.end() ), "producer votes must be sorted" );
            }

            account_votes_index_type avotes( SystemAccount, SystemAccount );
            auto ptr = avotes.find( vp.voter );

            eosio_assert( bool(ptr), "no stake to vote" );
            if ( ptr->i_am_proxy ) {
               eosio_assert( vp.proxy == 0 , "accounts elected to be proxy are not allowed to use another proxy" );
            }

            //find old producers, update old proxy if needed
            const std::vector<account_name>* old_producers = nullptr;
            if( ptr->proxy ) {
               if ( ptr->proxy == vp.proxy ) {
                  return; // nothing changed
               }
               auto old_proxy = avotes.find( ptr->proxy );
               avotes.update( *old_proxy, 0, [&](auto& a) { a.proxied_votes -= ptr->staked.quantity; } );
               old_producers = &old_proxy->producers;
            } else {
               old_producers = &ptr->producers;
            }

            //find new producers, update new proxy if needed
            const std::vector<account_name>* new_producers = nullptr;
            if ( vp.proxy ) {
               auto new_proxy = avotes.find( ptr->proxy );
               avotes.update( *new_proxy, 0, [&](auto& a) { a.proxied_votes += ptr->staked.quantity; } );
               new_producers = &new_proxy->producers;
            } else {
               new_producers = &vp.producers;
            }

            producer_info_index_type producers( SystemAccount, SystemAccount );

            //revoke votes only from no longer elected
            std::vector<account_name> revoked( old_producers->size() );
            auto end_it = std::set_difference( old_producers->begin(), old_producers->end(), new_producers->begin(), new_producers->end(), revoked.begin() );
            for ( auto it = revoked.begin(); it != end_it; ++it ) {
               producers.update( producers.get( *it ), 0, [&]( auto& pi ) { pi.total_votes -= ptr->staked.quantity; });
            }

            //update newly elected
            std::vector<account_name> elected( new_producers->size() );
            end_it = std::set_difference( new_producers->begin(), new_producers->end(), old_producers->begin(), old_producers->end(), elected.begin() );
            for ( auto it = elected.begin(); it != end_it; ++it ) {
               producers.update( producers.get( *it ), 0, [&]( auto& pi ) { pi.total_votes += ptr->staked.quantity; });
            }

            // save new values to the account itself
            avotes.update( *ptr, 0, [&](account_votes& a) {
                  a.proxy = vp.proxy;
                  a.last_update = now();
                  a.producers = vp.producers;
               });
         }

         ACTION( SystemAccount, register_proxy ) {
            account_name proxy_to_register;

            EOSLIB_SERIALIZE( register_proxy, (proxy_to_register) )
         };

         static void on( const register_proxy& reg ) {
            require_auth( reg.proxy_to_register );

            account_votes_index_type avotes( SystemAccount, SystemAccount );
            auto ptr = avotes.find( reg.proxy_to_register );
            if ( ptr ) {
               eosio_assert( ptr->i_am_proxy == 0, "account is already a proxy" );
               eosio_assert( ptr->proxy == 0, "account that uses a proxy is not allowed to become a proxy" );
               avotes.update( *ptr, 0, [&](account_votes& a) {
                     a.i_am_proxy = 1;
                     a.last_update = now();
                  });
            } else {
               avotes.emplace( reg.proxy_to_register, [&]( account_votes& a ) {
                     a.owner = reg.proxy_to_register;
                     a.last_update = now();
                     a.proxy = 0;
                     a.i_am_proxy = 1;
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }
         }

         struct block {};

         static void on( const block& ) {
         }
   };
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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.

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/win_util.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_util.h"
#include "net/url_request/url_request_unittest.h"

class TabRestoreUITest : public UITest {
 public:
  TabRestoreUITest() : UITest() {
    std::wstring path_prefix = test_data_directory_;
    file_util::AppendToPath(&path_prefix, L"session_history");
    path_prefix += FilePath::kSeparators[0];
    url1_ = net::FilePathToFileURL(path_prefix + L"bot1.html");
    url2_ = net::FilePathToFileURL(path_prefix + L"bot2.html");
  }

 protected:
  void RestoreTab() {
    int tab_count;

    // Reset browser_proxy to new window.
    scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
    ASSERT_TRUE(browser_proxy->GetTabCount(&tab_count));
    ASSERT_GT(tab_count, 0);

    // Restore the tab.
    ASSERT_TRUE(browser_proxy->ApplyAccelerator(IDC_RESTORE_TAB));

    // Get a handle to the restored tab.
    int restored_tab_count;
    ASSERT_TRUE(browser_proxy->WaitForTabCountToChange(tab_count,
                                                       &restored_tab_count,
                                                       5000));
    ASSERT_EQ(tab_count + 1, restored_tab_count);

    // Wait for the restored tab to finish loading.
    scoped_ptr<TabProxy> restored_tab_proxy(
        browser_proxy->GetTab(restored_tab_count - 1));
    ASSERT_TRUE(restored_tab_proxy->WaitForTabToBeRestored(kWaitForActionMsec));
  }

  GURL url1_;
  GURL url2_;

 private:
  DISALLOW_EVIL_CONSTRUCTORS(TabRestoreUITest);
};

TEST_F(TabRestoreUITest, Basic) {
  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));

  int initial_tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));

  // Add a tab
  browser_proxy->AppendTab(url1_);
  int new_tab_count;
  ASSERT_TRUE(browser_proxy->WaitForTabCountToChange(initial_tab_count,
                                                     &new_tab_count,
                                                     5000));
  scoped_ptr<TabProxy> new_tab(browser_proxy->GetTab(new_tab_count - 1));
  // Make sure we're at url.
  new_tab->NavigateToURL(url1_);
  // Close the tab.
  new_tab->Close(true);
  new_tab.reset();

  RestoreTab();

  // And make sure the URL matches.
  ASSERT_EQ(url1_, GetActiveTabURL());
}

TEST_F(TabRestoreUITest, RestoreToDifferentWindow) {
  // This test is disabled on win2k. See bug 1215881.
  if (win_util::GetWinVersion() == win_util::WINVERSION_2000)
    return;

  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));

  int tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&tab_count));

  // Close tabs until we only have one open.
  while (tab_count > 1) {
    scoped_ptr<TabProxy> tab_to_close(browser_proxy->GetTab(0));
    tab_to_close->Close(true);
  }

  // Navigate to url1 then url2.
  scoped_ptr<TabProxy> tab_proxy(browser_proxy->GetTab(0));
  tab_proxy->NavigateToURL(url1_);
  tab_proxy->NavigateToURL(url2_);

  // Create a new browser.
  ASSERT_TRUE(automation()->OpenNewBrowserWindow(SW_HIDE));
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2,
                                                       kWaitForActionMaxMsec));

  // Close the first browser.
  EXPECT_TRUE(tab_proxy->Close(true));
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(1,
                                                       kWaitForActionMaxMsec));

  // Tab and browser are no longer valid.
  tab_proxy.reset();
  browser_proxy.reset();

  RestoreTab();

  browser_proxy.reset(automation()->GetBrowserWindow(0));
  tab_proxy.reset(browser_proxy->GetActiveTab());
  // And make sure the URLs matches.
  ASSERT_EQ(url2_, GetActiveTabURL());
  ASSERT_TRUE(tab_proxy->GoBack());
  ASSERT_EQ(url1_, GetActiveTabURL());
}

// Tests that a duplicate history entry is not created when we restore a page
// to an existing SiteInstance.  (Bug 1230446)
TEST_F(TabRestoreUITest, RestoreWithExistingSiteInstance) {
  const wchar_t kDocRoot[] = L"chrome/test/data";
  TestServer server(kDocRoot);
  GURL http_url1(server.TestServerPageW(L"files/title1.html"));
  GURL http_url2(server.TestServerPageW(L"files/title2.html"));

  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  int initial_tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));

  // Add a tab
  browser_proxy->AppendTab(http_url1);
  int new_tab_count;
  ASSERT_TRUE(browser_proxy->WaitForTabCountToChange(initial_tab_count,
                                                     &new_tab_count,
                                                     5000));
  scoped_ptr<TabProxy> tab(browser_proxy->GetTab(new_tab_count - 1));

  // Navigate to another same-site URL.
  tab->NavigateToURL(http_url2);

  // Close the tab.
  tab->Close(true);
  tab.reset();

  // Create a new tab to the original site.  Assuming process-per-site is
  // enabled, this will ensure that the SiteInstance used by the restored tab
  // will already exist when the restore happens.
  browser_proxy->AppendTab(http_url2);

  // Restore the closed tab.
  RestoreTab();
  tab.reset(browser_proxy->GetActiveTab());

  // And make sure the URLs match.
  ASSERT_EQ(http_url2, GetActiveTabURL());
  ASSERT_TRUE(tab->GoBack());
  ASSERT_EQ(http_url1, GetActiveTabURL());
}

// Tests that the SiteInstances used for entries in a restored tab's history
// are given appropriate max page IDs, even if the renderer for the entry
// already exists.  (Bug 1204135)
TEST_F(TabRestoreUITest, RestoreCrossSiteWithExistingSiteInstance) {
  const wchar_t kDocRoot[] = L"chrome/test/data";
  TestServer server(kDocRoot);
  GURL http_url1(server.TestServerPageW(L"files/title1.html"));
  GURL http_url2(server.TestServerPageW(L"files/title2.html"));

  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  int initial_tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));

  // Add a tab
  browser_proxy->AppendTab(http_url1);
  int new_tab_count;
  ASSERT_TRUE(browser_proxy->WaitForTabCountToChange(initial_tab_count,
                                                     &new_tab_count,
                                                     5000));
  scoped_ptr<TabProxy> tab(browser_proxy->GetTab(new_tab_count - 1));

  // Navigate to more URLs, then a cross-site URL.
  tab->NavigateToURL(http_url2);
  tab->NavigateToURL(http_url1);
  tab->NavigateToURL(url1_);

  // Close the tab.
  tab->Close(true);
  tab.reset();

  // Create a new tab to the original site.  Assuming process-per-site is
  // enabled, this will ensure that the SiteInstance will already exist when
  // the user clicks Back in the restored tab.
  browser_proxy->AppendTab(http_url2);

  // Restore the closed tab.
  RestoreTab();
  tab.reset(browser_proxy->GetActiveTab());

  // And make sure the URLs match.
  ASSERT_EQ(url1_, GetActiveTabURL());
  ASSERT_TRUE(tab->GoBack());
  ASSERT_EQ(http_url1, GetActiveTabURL());

  // Navigating to a new URL should clear the forward list, because the max
  // page ID of the renderer should have been updated when we restored the tab.
  tab->NavigateToURL(http_url2);
  ASSERT_FALSE(tab->GoForward());
  ASSERT_EQ(http_url2, GetActiveTabURL());
}

TEST_F(TabRestoreUITest, RestoreWindow) {
  // Create a new window.
  int window_count;
  ASSERT_TRUE(automation()->GetBrowserWindowCount(&window_count));
  ASSERT_TRUE(automation()->OpenNewBrowserWindow(SW_HIDE));
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(++window_count,
                                                       kWaitForActionMaxMsec));

  // Create two more tabs, one with url1, the other url2.
  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  int initial_tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));
  browser_proxy->AppendTab(url1_);
  ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 1,
                                                     kWaitForActionMaxMsec));
  scoped_ptr<TabProxy> new_tab(browser_proxy->GetTab(initial_tab_count));
  new_tab->NavigateToURL(url1_);
  browser_proxy->AppendTab(url2_);
  ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 2,
                                                     kWaitForActionMaxMsec));
  new_tab.reset(browser_proxy->GetTab(initial_tab_count + 1));
  new_tab->NavigateToURL(url2_);

  // Close the window.
  ASSERT_TRUE(browser_proxy->ApplyAccelerator(IDC_CLOSE_WINDOW));
  browser_proxy.reset();
  new_tab.reset();
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(window_count - 1,
                                                       kWaitForActionMaxMsec));

  // Restore the window.
  browser_proxy.reset(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy->ApplyAccelerator(IDC_RESTORE_TAB));
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(window_count,
                                                       kWaitForActionMaxMsec));

  browser_proxy.reset(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 2,
                                                     kWaitForActionMaxMsec));

  scoped_ptr<TabProxy> restored_tab_proxy(
        browser_proxy->GetTab(initial_tab_count));
  ASSERT_TRUE(restored_tab_proxy->WaitForTabToBeRestored(kWaitForActionMsec));
  GURL url;
  ASSERT_TRUE(restored_tab_proxy->GetCurrentURL(&url));
  ASSERT_TRUE(url == url1_);

  restored_tab_proxy.reset(
        browser_proxy->GetTab(initial_tab_count + 1));
  ASSERT_TRUE(restored_tab_proxy->WaitForTabToBeRestored(kWaitForActionMsec));
  ASSERT_TRUE(restored_tab_proxy->GetCurrentURL(&url));
  ASSERT_TRUE(url == url2_);
}
<commit_msg>Make the tab restore ui_tests Purify friendly. Review URL: http://codereview.chromium.org/15053<commit_after>// Copyright (c) 2006-2008 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.

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/win_util.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_util.h"
#include "net/url_request/url_request_unittest.h"

class TabRestoreUITest : public UITest {
 public:
  TabRestoreUITest() : UITest() {
    std::wstring path_prefix = test_data_directory_;
    file_util::AppendToPath(&path_prefix, L"session_history");
    path_prefix += FilePath::kSeparators[0];
    url1_ = net::FilePathToFileURL(path_prefix + L"bot1.html");
    url2_ = net::FilePathToFileURL(path_prefix + L"bot2.html");
  }

 protected:
  void RestoreTab() {
    int tab_count;

    // Reset browser_proxy to new window.
    scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
    ASSERT_TRUE(browser_proxy->GetTabCount(&tab_count));
    ASSERT_GT(tab_count, 0);

    // Restore the tab.
    ASSERT_TRUE(browser_proxy->ApplyAccelerator(IDC_RESTORE_TAB));

    // Get a handle to the restored tab.
    int restored_tab_count;
    ASSERT_TRUE(browser_proxy->WaitForTabCountToChange(tab_count,
                                                       &restored_tab_count,
                                                       5000));
    ASSERT_EQ(tab_count + 1, restored_tab_count);

    // Wait for the restored tab to finish loading.
    scoped_ptr<TabProxy> restored_tab_proxy(
        browser_proxy->GetTab(restored_tab_count - 1));
    ASSERT_TRUE(restored_tab_proxy->WaitForTabToBeRestored(
        action_timeout_ms()));
  }

  GURL url1_;
  GURL url2_;

 private:
  DISALLOW_EVIL_CONSTRUCTORS(TabRestoreUITest);
};

TEST_F(TabRestoreUITest, Basic) {
  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));

  int initial_tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));

  // Add a tab
  browser_proxy->AppendTab(url1_);
  int new_tab_count;
  ASSERT_TRUE(browser_proxy->WaitForTabCountToChange(initial_tab_count,
                                                     &new_tab_count,
                                                     5000));
  scoped_ptr<TabProxy> new_tab(browser_proxy->GetTab(new_tab_count - 1));
  // Make sure we're at url.
  new_tab->NavigateToURL(url1_);
  // Close the tab.
  new_tab->Close(true);
  new_tab.reset();

  RestoreTab();

  // And make sure the URL matches.
  ASSERT_EQ(url1_, GetActiveTabURL());
}

TEST_F(TabRestoreUITest, RestoreToDifferentWindow) {
  // This test is disabled on win2k. See bug 1215881.
  if (win_util::GetWinVersion() == win_util::WINVERSION_2000)
    return;

  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));

  int tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&tab_count));

  // Close tabs until we only have one open.
  while (tab_count > 1) {
    scoped_ptr<TabProxy> tab_to_close(browser_proxy->GetTab(0));
    tab_to_close->Close(true);
  }

  // Navigate to url1 then url2.
  scoped_ptr<TabProxy> tab_proxy(browser_proxy->GetTab(0));
  tab_proxy->NavigateToURL(url1_);
  tab_proxy->NavigateToURL(url2_);

  // Create a new browser.
  ASSERT_TRUE(automation()->OpenNewBrowserWindow(SW_HIDE));
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2,
                                                       kWaitForActionMaxMsec));

  // Close the first browser.
  EXPECT_TRUE(tab_proxy->Close(true));
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(1,
                                                       kWaitForActionMaxMsec));

  // Tab and browser are no longer valid.
  tab_proxy.reset();
  browser_proxy.reset();

  RestoreTab();

  browser_proxy.reset(automation()->GetBrowserWindow(0));
  tab_proxy.reset(browser_proxy->GetActiveTab());
  // And make sure the URLs matches.
  ASSERT_EQ(url2_, GetActiveTabURL());
  ASSERT_TRUE(tab_proxy->GoBack());
  ASSERT_EQ(url1_, GetActiveTabURL());
}

// Tests that a duplicate history entry is not created when we restore a page
// to an existing SiteInstance.  (Bug 1230446)
TEST_F(TabRestoreUITest, RestoreWithExistingSiteInstance) {
  const wchar_t kDocRoot[] = L"chrome/test/data";
  TestServer server(kDocRoot);
  GURL http_url1(server.TestServerPageW(L"files/title1.html"));
  GURL http_url2(server.TestServerPageW(L"files/title2.html"));

  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  int initial_tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));

  // Add a tab
  browser_proxy->AppendTab(http_url1);
  int new_tab_count;
  ASSERT_TRUE(browser_proxy->WaitForTabCountToChange(initial_tab_count,
                                                     &new_tab_count,
                                                     5000));
  scoped_ptr<TabProxy> tab(browser_proxy->GetTab(new_tab_count - 1));

  // Navigate to another same-site URL.
  tab->NavigateToURL(http_url2);

  // Close the tab.
  tab->Close(true);
  tab.reset();

  // Create a new tab to the original site.  Assuming process-per-site is
  // enabled, this will ensure that the SiteInstance used by the restored tab
  // will already exist when the restore happens.
  browser_proxy->AppendTab(http_url2);

  // Restore the closed tab.
  RestoreTab();
  tab.reset(browser_proxy->GetActiveTab());

  // And make sure the URLs match.
  ASSERT_EQ(http_url2, GetActiveTabURL());
  ASSERT_TRUE(tab->GoBack());
  ASSERT_EQ(http_url1, GetActiveTabURL());
}

// Tests that the SiteInstances used for entries in a restored tab's history
// are given appropriate max page IDs, even if the renderer for the entry
// already exists.  (Bug 1204135)
TEST_F(TabRestoreUITest, RestoreCrossSiteWithExistingSiteInstance) {
  const wchar_t kDocRoot[] = L"chrome/test/data";
  TestServer server(kDocRoot);
  GURL http_url1(server.TestServerPageW(L"files/title1.html"));
  GURL http_url2(server.TestServerPageW(L"files/title2.html"));

  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  int initial_tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));

  // Add a tab
  browser_proxy->AppendTab(http_url1);
  int new_tab_count;
  ASSERT_TRUE(browser_proxy->WaitForTabCountToChange(initial_tab_count,
                                                     &new_tab_count,
                                                     5000));
  scoped_ptr<TabProxy> tab(browser_proxy->GetTab(new_tab_count - 1));

  // Navigate to more URLs, then a cross-site URL.
  tab->NavigateToURL(http_url2);
  tab->NavigateToURL(http_url1);
  tab->NavigateToURL(url1_);

  // Close the tab.
  tab->Close(true);
  tab.reset();

  // Create a new tab to the original site.  Assuming process-per-site is
  // enabled, this will ensure that the SiteInstance will already exist when
  // the user clicks Back in the restored tab.
  browser_proxy->AppendTab(http_url2);

  // Restore the closed tab.
  RestoreTab();
  tab.reset(browser_proxy->GetActiveTab());

  // And make sure the URLs match.
  ASSERT_EQ(url1_, GetActiveTabURL());
  ASSERT_TRUE(tab->GoBack());
  ASSERT_EQ(http_url1, GetActiveTabURL());

  // Navigating to a new URL should clear the forward list, because the max
  // page ID of the renderer should have been updated when we restored the tab.
  tab->NavigateToURL(http_url2);
  ASSERT_FALSE(tab->GoForward());
  ASSERT_EQ(http_url2, GetActiveTabURL());
}

TEST_F(TabRestoreUITest, RestoreWindow) {
  // Create a new window.
  int window_count;
  ASSERT_TRUE(automation()->GetBrowserWindowCount(&window_count));
  ASSERT_TRUE(automation()->OpenNewBrowserWindow(SW_HIDE));
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(++window_count,
                                                       kWaitForActionMaxMsec));

  // Create two more tabs, one with url1, the other url2.
  scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  int initial_tab_count;
  ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));
  browser_proxy->AppendTab(url1_);
  ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 1,
                                                     kWaitForActionMaxMsec));
  scoped_ptr<TabProxy> new_tab(browser_proxy->GetTab(initial_tab_count));
  new_tab->NavigateToURL(url1_);
  browser_proxy->AppendTab(url2_);
  ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 2,
                                                     kWaitForActionMaxMsec));
  new_tab.reset(browser_proxy->GetTab(initial_tab_count + 1));
  new_tab->NavigateToURL(url2_);

  // Close the window.
  ASSERT_TRUE(browser_proxy->ApplyAccelerator(IDC_CLOSE_WINDOW));
  browser_proxy.reset();
  new_tab.reset();
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(window_count - 1,
                                                       kWaitForActionMaxMsec));

  // Restore the window.
  browser_proxy.reset(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy->ApplyAccelerator(IDC_RESTORE_TAB));
  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(window_count,
                                                       kWaitForActionMaxMsec));

  browser_proxy.reset(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 2,
                                                     kWaitForActionMaxMsec));

  scoped_ptr<TabProxy> restored_tab_proxy(
        browser_proxy->GetTab(initial_tab_count));
  ASSERT_TRUE(restored_tab_proxy->WaitForTabToBeRestored(kWaitForActionMsec));
  GURL url;
  ASSERT_TRUE(restored_tab_proxy->GetCurrentURL(&url));
  ASSERT_TRUE(url == url1_);

  restored_tab_proxy.reset(
        browser_proxy->GetTab(initial_tab_count + 1));
  ASSERT_TRUE(restored_tab_proxy->WaitForTabToBeRestored(kWaitForActionMsec));
  ASSERT_TRUE(restored_tab_proxy->GetCurrentURL(&url));
  ASSERT_TRUE(url == url2_);
}
<|endoftext|>


<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.

#include "chrome/common/process_watcher.h"

#include "base/env_var.h"
#include "base/message_loop.h"
#include "base/object_watcher.h"
#include "chrome/common/env_vars.h"
#include "chrome/common/result_codes.h"

// Maximum amount of time (in milliseconds) to wait for the process to exit.
static const int kWaitInterval = 2000;

namespace {

class TimerExpiredTask : public Task, public base::ObjectWatcher::Delegate {
 public:
  explicit TimerExpiredTask(base::ProcessHandle process) : process_(process) {
    watcher_.StartWatching(process_, this);
  }

  virtual ~TimerExpiredTask() {
    if (process_) {
      KillProcess();
      DCHECK(!process_) << "Make sure to close the handle.";
    }
  }

  // Task ---------------------------------------------------------------------

  virtual void Run() {
    if (process_)
      KillProcess();
  }

  // MessageLoop::Watcher -----------------------------------------------------

  virtual void OnObjectSignaled(HANDLE object) {
    // When we're called from KillProcess, the ObjectWatcher may still be
    // watching.  the process handle, so make sure it has stopped.
    watcher_.StopWatching();

    CloseHandle(process_);
    process_ = NULL;
  }

 private:
  void KillProcess() {
    scoped_ptr<base::EnvVarGetter> env(base::EnvVarGetter::Create());
    if (env->HasEnv(env_vars::kHeadless)) {
     // If running the distributed tests, give the renderer a little time
     // to figure out that the channel is shutdown and unwind.
     if (WaitForSingleObject(process_, kWaitInterval) == WAIT_OBJECT_0) {
       OnObjectSignaled(process_);
       return;
     }
    }

    // OK, time to get frisky.  We don't actually care when the process
    // terminates.  We just care that it eventually terminates, and that's what
    // TerminateProcess should do for us. Don't check for the result code since
    // it fails quite often. This should be investigated eventually.
    base::KillProcess(process_, ResultCodes::HUNG, false);

    // Now, just cleanup as if the process exited normally.
    OnObjectSignaled(process_);
  }

  // The process that we are watching.
  base::ProcessHandle process_;

  base::ObjectWatcher watcher_;

  DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
};

}  // namespace

// static
void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
  DCHECK(process != GetCurrentProcess());

  // If already signaled, then we are done!
  if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
    CloseHandle(process);
    return;
  }

  MessageLoop::current()->PostDelayedTask(FROM_HERE,
                                          new TimerExpiredTask(process),
                                          kWaitInterval);
}
<commit_msg>Windows build fix<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.

#include "chrome/common/process_watcher.h"

#include "base/scoped_ptr.h"
#include "base/env_var.h"
#include "base/message_loop.h"
#include "base/object_watcher.h"
#include "chrome/common/env_vars.h"
#include "chrome/common/result_codes.h"

// Maximum amount of time (in milliseconds) to wait for the process to exit.
static const int kWaitInterval = 2000;

namespace {

class TimerExpiredTask : public Task, public base::ObjectWatcher::Delegate {
 public:
  explicit TimerExpiredTask(base::ProcessHandle process) : process_(process) {
    watcher_.StartWatching(process_, this);
  }

  virtual ~TimerExpiredTask() {
    if (process_) {
      KillProcess();
      DCHECK(!process_) << "Make sure to close the handle.";
    }
  }

  // Task ---------------------------------------------------------------------

  virtual void Run() {
    if (process_)
      KillProcess();
  }

  // MessageLoop::Watcher -----------------------------------------------------

  virtual void OnObjectSignaled(HANDLE object) {
    // When we're called from KillProcess, the ObjectWatcher may still be
    // watching.  the process handle, so make sure it has stopped.
    watcher_.StopWatching();

    CloseHandle(process_);
    process_ = NULL;
  }

 private:
  void KillProcess() {
    scoped_ptr<base::EnvVarGetter> env(base::EnvVarGetter::Create());
    if (env->HasEnv(env_vars::kHeadless)) {
     // If running the distributed tests, give the renderer a little time
     // to figure out that the channel is shutdown and unwind.
     if (WaitForSingleObject(process_, kWaitInterval) == WAIT_OBJECT_0) {
       OnObjectSignaled(process_);
       return;
     }
    }

    // OK, time to get frisky.  We don't actually care when the process
    // terminates.  We just care that it eventually terminates, and that's what
    // TerminateProcess should do for us. Don't check for the result code since
    // it fails quite often. This should be investigated eventually.
    base::KillProcess(process_, ResultCodes::HUNG, false);

    // Now, just cleanup as if the process exited normally.
    OnObjectSignaled(process_);
  }

  // The process that we are watching.
  base::ProcessHandle process_;

  base::ObjectWatcher watcher_;

  DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
};

}  // namespace

// static
void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
  DCHECK(process != GetCurrentProcess());

  // If already signaled, then we are done!
  if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
    CloseHandle(process);
    return;
  }

  MessageLoop::current()->PostDelayedTask(FROM_HERE,
                                          new TimerExpiredTask(process),
                                          kWaitInterval);
}
<|endoftext|>


<commit_before>//===--- CaptureTracking.cpp - Determine whether a pointer is captured ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains routines that help determine which pointers are captured.
// A pointer value is captured if the function makes a copy of any part of the
// pointer that outlives the call.  Not being captured means, more or less, that
// the pointer is only dereferenced and not stored in a global.  Returning part
// of the pointer as the function return value may or may not count as capturing
// the pointer, depending on the context.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/CallSite.h"

using namespace llvm;

CaptureTracker::~CaptureTracker() {}

bool CaptureTracker::shouldExplore(Use *U) { return true; }

namespace {
  struct SimpleCaptureTracker : public CaptureTracker {
    explicit SimpleCaptureTracker(bool ReturnCaptures)
      : ReturnCaptures(ReturnCaptures), Captured(false) {}

    void tooManyUses() { Captured = true; }

    bool captured(Use *U) {
      if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures)
        return false;

      Captured = true;
      return true;
    }

    bool ReturnCaptures;

    bool Captured;
  };
}

/// PointerMayBeCaptured - Return true if this pointer value may be captured
/// by the enclosing function (which is required to exist).  This routine can
/// be expensive, so consider caching the results.  The boolean ReturnCaptures
/// specifies whether returning the value (or part of it) from the function
/// counts as capturing it or not.  The boolean StoreCaptures specified whether
/// storing the value (or part of it) into memory anywhere automatically
/// counts as capturing it or not.
bool llvm::PointerMayBeCaptured(const Value *V,
                                bool ReturnCaptures, bool StoreCaptures) {
  assert(!isa<GlobalValue>(V) &&
         "It doesn't make sense to ask whether a global is captured.");

  // TODO: If StoreCaptures is not true, we could do Fancy analysis
  // to determine whether this store is not actually an escape point.
  // In that case, BasicAliasAnalysis should be updated as well to
  // take advantage of this.
  (void)StoreCaptures;

  SimpleCaptureTracker SCT(ReturnCaptures);
  PointerMayBeCaptured(V, &SCT);
  return SCT.Captured;
}

/// TODO: Write a new FunctionPass AliasAnalysis so that it can keep
/// a cache. Then we can move the code from BasicAliasAnalysis into
/// that path, and remove this threshold.
static int const Threshold = 20;

void llvm::PointerMayBeCaptured(const Value *V, CaptureTracker *Tracker) {
  assert(V->getType()->isPointerTy() && "Capture is for pointers only!");
  SmallVector<Use*, Threshold> Worklist;
  SmallSet<Use*, Threshold> Visited;
  int Count = 0;

  for (Value::const_use_iterator UI = V->use_begin(), UE = V->use_end();
       UI != UE; ++UI) {
    // If there are lots of uses, conservatively say that the value
    // is captured to avoid taking too much compile time.
    if (Count++ >= Threshold)
      return Tracker->tooManyUses();

    Use *U = &UI.getUse();
    if (!Tracker->shouldExplore(U)) continue;
    Visited.insert(U);
    Worklist.push_back(U);
  }

  while (!Worklist.empty()) {
    Use *U = Worklist.pop_back_val();
    Instruction *I = cast<Instruction>(U->getUser());
    V = U->get();

    switch (I->getOpcode()) {
    case Instruction::Call:
    case Instruction::Invoke: {
      CallSite CS(I);
      // Not captured if the callee is readonly, doesn't return a copy through
      // its return value and doesn't unwind (a readonly function can leak bits
      // by throwing an exception or not depending on the input value).
      if (CS.onlyReadsMemory() && CS.doesNotThrow() && I->getType()->isVoidTy())
        break;

      // Not captured if only passed via 'nocapture' arguments.  Note that
      // calling a function pointer does not in itself cause the pointer to
      // be captured.  This is a subtle point considering that (for example)
      // the callee might return its own address.  It is analogous to saying
      // that loading a value from a pointer does not cause the pointer to be
      // captured, even though the loaded value might be the pointer itself
      // (think of self-referential objects).
      CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
      for (CallSite::arg_iterator A = B; A != E; ++A)
        if (A->get() == V && !CS.doesNotCapture(A - B))
          // The parameter is not marked 'nocapture' - captured.
          if (Tracker->captured(U))
            return;
      break;
    }
    case Instruction::Load:
      // Loading from a pointer does not cause it to be captured.
      break;
    case Instruction::VAArg:
      // "va-arg" from a pointer does not cause it to be captured.
      break;
    case Instruction::Store:
      if (V == I->getOperand(0))
        // Stored the pointer - conservatively assume it may be captured.
        if (Tracker->captured(U))
          return;
      // Storing to the pointee does not cause the pointer to be captured.
      break;
    case Instruction::BitCast:
    case Instruction::GetElementPtr:
    case Instruction::PHI:
    case Instruction::Select:
      // The original value is not captured via this if the new value isn't.
      for (Instruction::use_iterator UI = I->use_begin(), UE = I->use_end();
           UI != UE; ++UI) {
        Use *U = &UI.getUse();
        if (Visited.insert(U))
          if (Tracker->shouldExplore(U))
            Worklist.push_back(U);
      }
      break;
    case Instruction::ICmp:
      // Don't count comparisons of a no-alias return value against null as
      // captures. This allows us to ignore comparisons of malloc results
      // with null, for example.
      if (isNoAliasCall(V->stripPointerCasts()))
        if (ConstantPointerNull *CPN =
              dyn_cast<ConstantPointerNull>(I->getOperand(1)))
          if (CPN->getType()->getAddressSpace() == 0)
            break;
      // Otherwise, be conservative. There are crazy ways to capture pointers
      // using comparisons.
      if (Tracker->captured(U))
        return;
      break;
    default:
      // Something else - be conservative and say it is captured.
      if (Tracker->captured(U))
        return;
      break;
    }
  }

  // All uses examined.
}
<commit_msg>MFC r258350:<commit_after>//===--- CaptureTracking.cpp - Determine whether a pointer is captured ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains routines that help determine which pointers are captured.
// A pointer value is captured if the function makes a copy of any part of the
// pointer that outlives the call.  Not being captured means, more or less, that
// the pointer is only dereferenced and not stored in a global.  Returning part
// of the pointer as the function return value may or may not count as capturing
// the pointer, depending on the context.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/CallSite.h"

using namespace llvm;

CaptureTracker::~CaptureTracker() {}

bool CaptureTracker::shouldExplore(Use *U) { return true; }

namespace {
  struct SimpleCaptureTracker : public CaptureTracker {
    explicit SimpleCaptureTracker(bool ReturnCaptures)
      : ReturnCaptures(ReturnCaptures), Captured(false) {}

    void tooManyUses() { Captured = true; }

    bool captured(Use *U) {
      if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures)
        return false;

      Captured = true;
      return true;
    }

    bool ReturnCaptures;

    bool Captured;
  };
}

/// PointerMayBeCaptured - Return true if this pointer value may be captured
/// by the enclosing function (which is required to exist).  This routine can
/// be expensive, so consider caching the results.  The boolean ReturnCaptures
/// specifies whether returning the value (or part of it) from the function
/// counts as capturing it or not.  The boolean StoreCaptures specified whether
/// storing the value (or part of it) into memory anywhere automatically
/// counts as capturing it or not.
bool llvm::PointerMayBeCaptured(const Value *V,
                                bool ReturnCaptures, bool StoreCaptures) {
  assert(!isa<GlobalValue>(V) &&
         "It doesn't make sense to ask whether a global is captured.");

  // TODO: If StoreCaptures is not true, we could do Fancy analysis
  // to determine whether this store is not actually an escape point.
  // In that case, BasicAliasAnalysis should be updated as well to
  // take advantage of this.
  (void)StoreCaptures;

  SimpleCaptureTracker SCT(ReturnCaptures);
  PointerMayBeCaptured(V, &SCT);
  return SCT.Captured;
}

/// TODO: Write a new FunctionPass AliasAnalysis so that it can keep
/// a cache. Then we can move the code from BasicAliasAnalysis into
/// that path, and remove this threshold.
static int const Threshold = 20;

void llvm::PointerMayBeCaptured(const Value *V, CaptureTracker *Tracker) {
  assert(V->getType()->isPointerTy() && "Capture is for pointers only!");
  SmallVector<Use*, Threshold> Worklist;
  SmallSet<Use*, Threshold> Visited;
  int Count = 0;

  for (Value::const_use_iterator UI = V->use_begin(), UE = V->use_end();
       UI != UE; ++UI) {
    // If there are lots of uses, conservatively say that the value
    // is captured to avoid taking too much compile time.
    if (Count++ >= Threshold)
      return Tracker->tooManyUses();

    Use *U = &UI.getUse();
    if (!Tracker->shouldExplore(U)) continue;
    Visited.insert(U);
    Worklist.push_back(U);
  }

  while (!Worklist.empty()) {
    Use *U = Worklist.pop_back_val();
    Instruction *I = cast<Instruction>(U->getUser());
    V = U->get();

    switch (I->getOpcode()) {
    case Instruction::Call:
    case Instruction::Invoke: {
      CallSite CS(I);
      // Not captured if the callee is readonly, doesn't return a copy through
      // its return value and doesn't unwind (a readonly function can leak bits
      // by throwing an exception or not depending on the input value).
      if (CS.onlyReadsMemory() && CS.doesNotThrow() && I->getType()->isVoidTy())
        break;

      // Not captured if only passed via 'nocapture' arguments.  Note that
      // calling a function pointer does not in itself cause the pointer to
      // be captured.  This is a subtle point considering that (for example)
      // the callee might return its own address.  It is analogous to saying
      // that loading a value from a pointer does not cause the pointer to be
      // captured, even though the loaded value might be the pointer itself
      // (think of self-referential objects).
      CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
      for (CallSite::arg_iterator A = B; A != E; ++A)
        if (A->get() == V && !CS.doesNotCapture(A - B))
          // The parameter is not marked 'nocapture' - captured.
          if (Tracker->captured(U))
            return;
      break;
    }
    case Instruction::Load:
      // Loading from a pointer does not cause it to be captured.
      break;
    case Instruction::VAArg:
      // "va-arg" from a pointer does not cause it to be captured.
      break;
    case Instruction::Store:
      if (V == I->getOperand(0))
        // Stored the pointer - conservatively assume it may be captured.
        if (Tracker->captured(U))
          return;
      // Storing to the pointee does not cause the pointer to be captured.
      break;
    case Instruction::BitCast:
    case Instruction::GetElementPtr:
    case Instruction::PHI:
    case Instruction::Select:
      // The original value is not captured via this if the new value isn't.
      Count = 0;
      for (Instruction::use_iterator UI = I->use_begin(), UE = I->use_end();
           UI != UE; ++UI) {
        // If there are lots of uses, conservatively say that the value
        // is captured to avoid taking too much compile time.
        if (Count++ >= Threshold)
          return Tracker->tooManyUses();

        Use *U = &UI.getUse();
        if (Visited.insert(U))
          if (Tracker->shouldExplore(U))
            Worklist.push_back(U);
      }
      break;
    case Instruction::ICmp:
      // Don't count comparisons of a no-alias return value against null as
      // captures. This allows us to ignore comparisons of malloc results
      // with null, for example.
      if (isNoAliasCall(V->stripPointerCasts()))
        if (ConstantPointerNull *CPN =
              dyn_cast<ConstantPointerNull>(I->getOperand(1)))
          if (CPN->getType()->getAddressSpace() == 0)
            break;
      // Otherwise, be conservative. There are crazy ways to capture pointers
      // using comparisons.
      if (Tracker->captured(U))
        return;
      break;
    default:
      // Something else - be conservative and say it is captured.
      if (Tracker->captured(U))
        return;
      break;
    }
  }

  // All uses examined.
}
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Karoliina T. Salminen <karoliina.t.salminen@nokia.com>
**
** This file is part of duicontrolpanel.
**
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "qfileinfo-fake.h"

#include <QSet>
#include <QHash>
#include <QFileInfo>
#include <QString>

static QSet<QString> existingFiles;
static QHash<const QFileInfo*, QString> priv;
class QFileInfoPrivate {};

void qfileinfo_setExists (const QString& filePath, bool exists)
{
    if (exists) {
        existingFiles.insert (filePath);
    } else {
        existingFiles.remove (filePath);
    }
}

QFileInfo::QFileInfo ( const QString & file )
{
    priv[this] = file;
}

bool QFileInfo::exists () const
{
    return existingFiles.contains (this->filePath());
}

QString QFileInfo::filePath () const
{
    return priv[this];
}

<commit_msg>Little compile fix<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Karoliina T. Salminen <karoliina.t.salminen@nokia.com>
**
** This file is part of duicontrolpanel.
**
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "qfileinfo-fake.h"

#include <QSet>
#include <QHash>
#include <QFileInfo>
#include <QString>
#include <QSharedData>

static QSet<QString> existingFiles;
static QHash<const QFileInfo*, QString> priv;
class QFileInfoPrivate: public QSharedData {};

void qfileinfo_setExists (const QString& filePath, bool exists)
{
    if (exists) {
        existingFiles.insert (filePath);
    } else {
        existingFiles.remove (filePath);
    }
}

QFileInfo::QFileInfo ( const QString & file )
{
    priv[this] = file;
}

bool QFileInfo::exists () const
{
    return existingFiles.contains (this->filePath());
}

QString QFileInfo::filePath () const
{
    return priv[this];
}

<|endoftext|>