<commit_before>/**
 * \file
 * \brief MutexControlBlock class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-11-07
 */

#include "distortos/scheduler/MutexControlBlock.hpp"

#include "distortos/scheduler/getScheduler.hpp"
#include "distortos/scheduler/Scheduler.hpp"

namespace distortos
{

namespace scheduler
{

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

MutexControlBlock::MutexControlBlock(const Protocol protocol, const uint8_t priorityCeiling) :
		blockedList_{getScheduler().getThreadControlBlockListAllocator(), ThreadControlBlock::State::BlockedOnMutex},
		list_{},
		iterator_{},
		owner_{},
		protocol_{protocol},
		priorityCeiling_{priorityCeiling}
{

}

void MutexControlBlock::block()
{
	getScheduler().block(blockedList_);
}

int MutexControlBlock::blockUntil(const TickClock::time_point timePoint)
{
	return getScheduler().blockUntil(blockedList_, timePoint);
}

uint8_t MutexControlBlock::getBoostedPriority() const
{
	if (protocol_ == Protocol::PriorityInheritance)
	{
		if (blockedList_.empty() == true)
			return 0;
		return blockedList_.begin()->get().getEffectivePriority();
	}

	if (protocol_ == Protocol::PriorityProtect)
		return priorityCeiling_;

	return 0;
}

void MutexControlBlock::lock()
{
	auto& scheduler = scheduler::getScheduler();
	owner_ = &scheduler.getCurrentThreadControlBlock();

	if (protocol_ == Protocol::None)
		return;

	scheduler.getMutexControlBlockListAllocatorPool().feed(link_);
	list_ = &owner_->getOwnedProtocolMutexControlBlocksList();
	list_->emplace_front(*this);
	iterator_ = list_->begin();

	if (protocol_ == Protocol::PriorityProtect)
		owner_->updateBoostedPriority();
}

void MutexControlBlock::unlockOrTransferLock()
{
	auto& oldOwner = *owner_;

	if (blockedList_.empty() == false)
		transferLock();
	else
		unlock();

	if (protocol_ == Protocol::None)
		return;

	oldOwner.updateBoostedPriority();

	if (owner_ == nullptr)
		return;

	owner_->updateBoostedPriority();
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

void MutexControlBlock::priorityInheritanceBeforeBlock() const
{
	// calling thread is not yet on the blocked list, that's why it's effective priority is given explicitly
	owner_->updateBoostedPriority(getScheduler().getCurrentThreadControlBlock().getEffectivePriority());
}

void MutexControlBlock::transferLock()
{
	owner_ = &blockedList_.begin()->get();	// pass ownership to the unblocked thread
	scheduler::getScheduler().unblock(blockedList_.begin());

	if (list_ == nullptr)
		return;

	auto& oldList = *list_;
	list_ = &owner_->getOwnedProtocolMutexControlBlocksList();
	list_->splice(list_->begin(), oldList, iterator_);
}

void MutexControlBlock::unlock()
{
	owner_ = nullptr;

	if (list_ == nullptr)
		return;

	list_->erase(iterator_);
	list_ = nullptr;
}

}	// namespace scheduler

}	// namespace distortos
<commit_msg>MutexControlBlock: call MutexControlBlock::priorityInheritanceBeforeBlock() in MutexControlBlock::block() and MutexControlBlock::blockUntil()<commit_after>/**
 * \file
 * \brief MutexControlBlock class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-11-07
 */

#include "distortos/scheduler/MutexControlBlock.hpp"

#include "distortos/scheduler/getScheduler.hpp"
#include "distortos/scheduler/Scheduler.hpp"

namespace distortos
{

namespace scheduler
{

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

MutexControlBlock::MutexControlBlock(const Protocol protocol, const uint8_t priorityCeiling) :
		blockedList_{getScheduler().getThreadControlBlockListAllocator(), ThreadControlBlock::State::BlockedOnMutex},
		list_{},
		iterator_{},
		owner_{},
		protocol_{protocol},
		priorityCeiling_{priorityCeiling}
{

}

void MutexControlBlock::block()
{
	if (protocol_ == Protocol::PriorityInheritance)
		priorityInheritanceBeforeBlock();

	getScheduler().block(blockedList_);
}

int MutexControlBlock::blockUntil(const TickClock::time_point timePoint)
{
	if (protocol_ == Protocol::PriorityInheritance)
		priorityInheritanceBeforeBlock();

	return getScheduler().blockUntil(blockedList_, timePoint);
}

uint8_t MutexControlBlock::getBoostedPriority() const
{
	if (protocol_ == Protocol::PriorityInheritance)
	{
		if (blockedList_.empty() == true)
			return 0;
		return blockedList_.begin()->get().getEffectivePriority();
	}

	if (protocol_ == Protocol::PriorityProtect)
		return priorityCeiling_;

	return 0;
}

void MutexControlBlock::lock()
{
	auto& scheduler = scheduler::getScheduler();
	owner_ = &scheduler.getCurrentThreadControlBlock();

	if (protocol_ == Protocol::None)
		return;

	scheduler.getMutexControlBlockListAllocatorPool().feed(link_);
	list_ = &owner_->getOwnedProtocolMutexControlBlocksList();
	list_->emplace_front(*this);
	iterator_ = list_->begin();

	if (protocol_ == Protocol::PriorityProtect)
		owner_->updateBoostedPriority();
}

void MutexControlBlock::unlockOrTransferLock()
{
	auto& oldOwner = *owner_;

	if (blockedList_.empty() == false)
		transferLock();
	else
		unlock();

	if (protocol_ == Protocol::None)
		return;

	oldOwner.updateBoostedPriority();

	if (owner_ == nullptr)
		return;

	owner_->updateBoostedPriority();
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

void MutexControlBlock::priorityInheritanceBeforeBlock() const
{
	// calling thread is not yet on the blocked list, that's why it's effective priority is given explicitly
	owner_->updateBoostedPriority(getScheduler().getCurrentThreadControlBlock().getEffectivePriority());
}

void MutexControlBlock::transferLock()
{
	owner_ = &blockedList_.begin()->get();	// pass ownership to the unblocked thread
	scheduler::getScheduler().unblock(blockedList_.begin());

	if (list_ == nullptr)
		return;

	auto& oldList = *list_;
	list_ = &owner_->getOwnedProtocolMutexControlBlocksList();
	list_->splice(list_->begin(), oldList, iterator_);
}

void MutexControlBlock::unlock()
{
	owner_ = nullptr;

	if (list_ == nullptr)
		return;

	list_->erase(iterator_);
	list_ = nullptr;
}

}	// namespace scheduler

}	// namespace distortos
<|endoftext|>


<commit_before>/*
 *  Copyright 2008-2012 NVIDIA Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*! \file reduce.inl
 *  \brief Inline file for reduce.h
 */

#include <thrust/detail/config.h>
#include <thrust/distance.h>
#include <thrust/iterator/iterator_traits.h>
#include <thrust/detail/minmax.h>
#include <thrust/detail/temporary_array.h>
#include <thrust/system/detail/generic/select_system.h>

#include <thrust/system/cuda/detail/runtime_introspection.h>
#include <thrust/system/cuda/detail/extern_shared_ptr.h>
#include <thrust/system/cuda/detail/block/reduce.h>
#include <thrust/system/cuda/detail/detail/launch_closure.h>
#include <thrust/system/cuda/detail/detail/launch_calculator.h>
#include <thrust/system/cuda/detail/execution_policy.h>
#include <thrust/detail/seq.h>

namespace thrust
{
namespace system
{
namespace cuda
{
namespace detail
{
namespace reduce_detail
{

/*
 * Reduce a vector of n elements using binary_op()
 *
 * The order of reduction is not defined, so binary_op() should
 * be a commutative (and associative) operator such as
 * (integer) addition.  Since floating point operations
 * do not completely satisfy these criteria, the result is
 * generally not the same as a consecutive reduction of
 * the elements.
 *
 * Uses the same pattern as reduce6() in the CUDA SDK
 *
 */
template<typename InputIterator,
         typename Size,
         typename T,
         typename OutputIterator,
         typename BinaryFunction,
         typename Context>
struct unordered_reduce_closure
{
  InputIterator  input;
  Size           n;
  T              init;
  OutputIterator output;
  BinaryFunction binary_op;
  unsigned int shared_array_size;

  typedef Context context_type;
  context_type context;

  __host__ __device__
  unordered_reduce_closure(InputIterator input, Size n, T init, OutputIterator output, BinaryFunction binary_op, unsigned int shared_array_size, Context context = Context())
    : input(input), n(n), init(init), output(output), binary_op(binary_op), shared_array_size(shared_array_size), context(context) {}

  __device__ __thrust_forceinline__
  void operator()(void)
  {
    typedef typename thrust::iterator_value<OutputIterator>::type OutputType;
    extern_shared_ptr<OutputType>  shared_array;

    Size grid_size = context.block_dimension() * context.grid_dimension();

    Size i = context.linear_index();

    input += i;

    // compute reduction with all blockDim.x threads
    OutputType sum = thrust::raw_reference_cast(*input);

    i     += grid_size;
    input += grid_size;

    while (i < n)
    {
      OutputType val = thrust::raw_reference_cast(*input);

      sum = binary_op(sum, val);

      i      += grid_size;
      input  += grid_size;
    }

    // write first shared_array_size values into shared memory
    if (context.thread_index() < shared_array_size)
      shared_array[context.thread_index()] = sum;

    // accumulate remaining values (if any) to shared memory in stages
    if (context.block_dimension() > shared_array_size)
    {
      unsigned int lb = shared_array_size;
      unsigned int ub = shared_array_size + lb;

      while (lb < context.block_dimension())
      {
        context.barrier();

        if (lb <= context.thread_index() && context.thread_index() < ub)
        {
          OutputType tmp = shared_array[context.thread_index() - lb];
          shared_array[context.thread_index() - lb] = binary_op(tmp, sum);
        }

        lb += shared_array_size;
        ub += shared_array_size;
      }
    }

    context.barrier();

    block::reduce_n(context, shared_array, thrust::min<unsigned int>(context.block_dimension(), shared_array_size), binary_op);

    if (context.thread_index() == 0)
    {
      OutputType tmp = shared_array[0];

      if (context.grid_dimension() == 1)
        tmp = binary_op(init, tmp);

      output += context.block_index();
      *output = tmp;
    }
  }
};

__THRUST_DISABLE_MSVC_POSSIBLE_LOSS_OF_DATA_WARNING_BEGIN

template<typename DerivedPolicy,
         typename InputIterator,
         typename OutputType,
         typename BinaryFunction>
__host__ __device__
OutputType reduce(execution_policy<DerivedPolicy> &exec,
                  InputIterator first,
                  InputIterator last,
                  OutputType init,
                  BinaryFunction binary_op)
{
  // we're attempting to launch a kernel, assert we're compiling with nvcc
  // ========================================================================
  // X Note to the user: If you've found this line due to a compiler error, X
  // X you need to compile your code using nvcc, rather than g++ or cl.exe  X
  // ========================================================================
  THRUST_STATIC_ASSERT( (thrust::detail::depend_on_instantiation<InputIterator, THRUST_DEVICE_COMPILER == THRUST_DEVICE_COMPILER_NVCC>::value) );

  typedef typename thrust::iterator_difference<InputIterator>::type difference_type;

  difference_type n = thrust::distance(first,last);

  if (n == 0)
    return init;

  typedef thrust::detail::temporary_array<OutputType, DerivedPolicy> OutputArray;
  typedef typename OutputArray::iterator OutputIterator;

  typedef detail::blocked_thread_array Context;
  typedef unordered_reduce_closure<InputIterator,difference_type,OutputType,OutputIterator,BinaryFunction,Context> Closure;

  function_attributes_t attributes = detail::closure_attributes<Closure>();

  // TODO chose this in a more principled manner
  size_t threshold = thrust::max<size_t>(2 * attributes.maxThreadsPerBlock, 1024);

  device_properties_t properties = device_properties();

  // launch configuration
  size_t num_blocks;
  size_t block_size;
  size_t array_size;
  size_t smem_bytes;

  // first level reduction
  if(static_cast<size_t>(n) < threshold)
  {
    num_blocks = 1;
    block_size = thrust::min(static_cast<size_t>(n), static_cast<size_t>(attributes.maxThreadsPerBlock));
    array_size = thrust::min(block_size, (properties.sharedMemPerBlock - attributes.sharedSizeBytes) / sizeof(OutputType));
    smem_bytes = sizeof(OutputType) * array_size;
  }
  else
  {
    detail::launch_calculator<Closure> calculator;

    thrust::tuple<size_t,size_t,size_t> config = calculator.with_variable_block_size_available_smem();

    num_blocks = thrust::min(thrust::get<0>(config), static_cast<size_t>(n) / thrust::get<1>(config));
    block_size = thrust::get<1>(config);
    array_size = thrust::min(block_size, thrust::get<2>(config) / sizeof(OutputType));
    smem_bytes = sizeof(OutputType) * array_size;
  }

  // TODO assert(n <= num_blocks * block_size);
  // TODO if (shared_array_size < 1) throw cuda exception "insufficient shared memory"

  OutputArray output(exec, num_blocks);

  Closure closure(first, n, init, output.begin(), binary_op, array_size);

  //std::cout << "Launching " << num_blocks << " blocks of kernel with " << block_size << " threads and " << smem_bytes << " shared memory per block " << std::endl;

  detail::launch_closure(closure, num_blocks, block_size, smem_bytes);

  // second level reduction
  if (num_blocks > 1)
  {
    typedef detail::blocked_thread_array Context;
    typedef unordered_reduce_closure<OutputIterator,difference_type,OutputType,OutputIterator,BinaryFunction,Context> Closure;

    function_attributes_t attributes = detail::closure_attributes<Closure>();

    num_blocks = 1;
    block_size = thrust::min(output.size(), static_cast<size_t>(attributes.maxThreadsPerBlock));
    array_size = thrust::min(block_size, (properties.sharedMemPerBlock - attributes.sharedSizeBytes) / sizeof(OutputType));
    smem_bytes = sizeof(OutputType) * array_size;

    // TODO if (shared_array_size < 1) throw cuda exception "insufficient shared memory"

    Closure closure(output.begin(), output.size(), init, output.begin(), binary_op, array_size);

    //std::cout << "Launching " << num_blocks << " blocks of kernel with " << block_size << " threads and " << smem_bytes << " shared memory per block " << std::endl;

    detail::launch_closure(closure, num_blocks, block_size, smem_bytes);
  }

  return output[0];
} // end reduce

} // end reduce_detail

__THRUST_DISABLE_MSVC_POSSIBLE_LOSS_OF_DATA_WARNING_END

template<typename DerivedPolicy,
         typename InputIterator,
         typename OutputType,
         typename BinaryFunction>
__host__ __device__
OutputType reduce(execution_policy<DerivedPolicy> &exec,
                  InputIterator first,
                  InputIterator last,
                  OutputType init,
                  BinaryFunction binary_op)
{
  struct workaround
  {
    __host__ __device__
    static OutputType parallel_path(execution_policy<DerivedPolicy> &exec, InputIterator first, InputIterator last, OutputType init, BinaryFunction binary_op)
    {
      return reduce_detail::reduce(exec, first, last, init, binary_op);
    }

    __host__ __device__
    static OutputType sequential_path(execution_policy<DerivedPolicy> &, InputIterator first, InputIterator last, OutputType init, BinaryFunction binary_op)
    {
      return thrust::reduce(thrust::seq, first, last, init, binary_op);
    }
  };

#if !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 350)
  return workaround::parallel_path(exec, first, last, init, binary_op);
#else
  return workaround::sequential_path(exec, first, last, init, binary_op);
#endif
} // end reduce()

} // end namespace detail
} // end namespace cuda
} // end namespace system
} // end namespace thrust

<commit_msg>Add missing header.<commit_after>/*
 *  Copyright 2008-2012 NVIDIA Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*! \file reduce.inl
 *  \brief Inline file for reduce.h
 */

#include <thrust/detail/config.h>
#include <thrust/distance.h>
#include <thrust/iterator/iterator_traits.h>
#include <thrust/detail/minmax.h>
#include <thrust/detail/temporary_array.h>
#include <thrust/system/detail/generic/select_system.h>

#include <thrust/system/cuda/detail/runtime_introspection.h>
#include <thrust/system/cuda/detail/extern_shared_ptr.h>
#include <thrust/system/cuda/detail/block/reduce.h>
#include <thrust/system/cuda/detail/detail/launch_closure.h>
#include <thrust/system/cuda/detail/detail/launch_calculator.h>
#include <thrust/system/cuda/detail/execution_policy.h>
#include <thrust/reduce.h>
#include <thrust/detail/seq.h>

namespace thrust
{
namespace system
{
namespace cuda
{
namespace detail
{
namespace reduce_detail
{

/*
 * Reduce a vector of n elements using binary_op()
 *
 * The order of reduction is not defined, so binary_op() should
 * be a commutative (and associative) operator such as
 * (integer) addition.  Since floating point operations
 * do not completely satisfy these criteria, the result is
 * generally not the same as a consecutive reduction of
 * the elements.
 *
 * Uses the same pattern as reduce6() in the CUDA SDK
 *
 */
template<typename InputIterator,
         typename Size,
         typename T,
         typename OutputIterator,
         typename BinaryFunction,
         typename Context>
struct unordered_reduce_closure
{
  InputIterator  input;
  Size           n;
  T              init;
  OutputIterator output;
  BinaryFunction binary_op;
  unsigned int shared_array_size;

  typedef Context context_type;
  context_type context;

  __host__ __device__
  unordered_reduce_closure(InputIterator input, Size n, T init, OutputIterator output, BinaryFunction binary_op, unsigned int shared_array_size, Context context = Context())
    : input(input), n(n), init(init), output(output), binary_op(binary_op), shared_array_size(shared_array_size), context(context) {}

  __device__ __thrust_forceinline__
  void operator()(void)
  {
    typedef typename thrust::iterator_value<OutputIterator>::type OutputType;
    extern_shared_ptr<OutputType>  shared_array;

    Size grid_size = context.block_dimension() * context.grid_dimension();

    Size i = context.linear_index();

    input += i;

    // compute reduction with all blockDim.x threads
    OutputType sum = thrust::raw_reference_cast(*input);

    i     += grid_size;
    input += grid_size;

    while (i < n)
    {
      OutputType val = thrust::raw_reference_cast(*input);

      sum = binary_op(sum, val);

      i      += grid_size;
      input  += grid_size;
    }

    // write first shared_array_size values into shared memory
    if (context.thread_index() < shared_array_size)
      shared_array[context.thread_index()] = sum;

    // accumulate remaining values (if any) to shared memory in stages
    if (context.block_dimension() > shared_array_size)
    {
      unsigned int lb = shared_array_size;
      unsigned int ub = shared_array_size + lb;

      while (lb < context.block_dimension())
      {
        context.barrier();

        if (lb <= context.thread_index() && context.thread_index() < ub)
        {
          OutputType tmp = shared_array[context.thread_index() - lb];
          shared_array[context.thread_index() - lb] = binary_op(tmp, sum);
        }

        lb += shared_array_size;
        ub += shared_array_size;
      }
    }

    context.barrier();

    block::reduce_n(context, shared_array, thrust::min<unsigned int>(context.block_dimension(), shared_array_size), binary_op);

    if (context.thread_index() == 0)
    {
      OutputType tmp = shared_array[0];

      if (context.grid_dimension() == 1)
        tmp = binary_op(init, tmp);

      output += context.block_index();
      *output = tmp;
    }
  }
};

__THRUST_DISABLE_MSVC_POSSIBLE_LOSS_OF_DATA_WARNING_BEGIN

template<typename DerivedPolicy,
         typename InputIterator,
         typename OutputType,
         typename BinaryFunction>
__host__ __device__
OutputType reduce(execution_policy<DerivedPolicy> &exec,
                  InputIterator first,
                  InputIterator last,
                  OutputType init,
                  BinaryFunction binary_op)
{
  // we're attempting to launch a kernel, assert we're compiling with nvcc
  // ========================================================================
  // X Note to the user: If you've found this line due to a compiler error, X
  // X you need to compile your code using nvcc, rather than g++ or cl.exe  X
  // ========================================================================
  THRUST_STATIC_ASSERT( (thrust::detail::depend_on_instantiation<InputIterator, THRUST_DEVICE_COMPILER == THRUST_DEVICE_COMPILER_NVCC>::value) );

  typedef typename thrust::iterator_difference<InputIterator>::type difference_type;

  difference_type n = thrust::distance(first,last);

  if (n == 0)
    return init;

  typedef thrust::detail::temporary_array<OutputType, DerivedPolicy> OutputArray;
  typedef typename OutputArray::iterator OutputIterator;

  typedef detail::blocked_thread_array Context;
  typedef unordered_reduce_closure<InputIterator,difference_type,OutputType,OutputIterator,BinaryFunction,Context> Closure;

  function_attributes_t attributes = detail::closure_attributes<Closure>();

  // TODO chose this in a more principled manner
  size_t threshold = thrust::max<size_t>(2 * attributes.maxThreadsPerBlock, 1024);

  device_properties_t properties = device_properties();

  // launch configuration
  size_t num_blocks;
  size_t block_size;
  size_t array_size;
  size_t smem_bytes;

  // first level reduction
  if(static_cast<size_t>(n) < threshold)
  {
    num_blocks = 1;
    block_size = thrust::min(static_cast<size_t>(n), static_cast<size_t>(attributes.maxThreadsPerBlock));
    array_size = thrust::min(block_size, (properties.sharedMemPerBlock - attributes.sharedSizeBytes) / sizeof(OutputType));
    smem_bytes = sizeof(OutputType) * array_size;
  }
  else
  {
    detail::launch_calculator<Closure> calculator;

    thrust::tuple<size_t,size_t,size_t> config = calculator.with_variable_block_size_available_smem();

    num_blocks = thrust::min(thrust::get<0>(config), static_cast<size_t>(n) / thrust::get<1>(config));
    block_size = thrust::get<1>(config);
    array_size = thrust::min(block_size, thrust::get<2>(config) / sizeof(OutputType));
    smem_bytes = sizeof(OutputType) * array_size;
  }

  // TODO assert(n <= num_blocks * block_size);
  // TODO if (shared_array_size < 1) throw cuda exception "insufficient shared memory"

  OutputArray output(exec, num_blocks);

  Closure closure(first, n, init, output.begin(), binary_op, array_size);

  //std::cout << "Launching " << num_blocks << " blocks of kernel with " << block_size << " threads and " << smem_bytes << " shared memory per block " << std::endl;

  detail::launch_closure(closure, num_blocks, block_size, smem_bytes);

  // second level reduction
  if (num_blocks > 1)
  {
    typedef detail::blocked_thread_array Context;
    typedef unordered_reduce_closure<OutputIterator,difference_type,OutputType,OutputIterator,BinaryFunction,Context> Closure;

    function_attributes_t attributes = detail::closure_attributes<Closure>();

    num_blocks = 1;
    block_size = thrust::min(output.size(), static_cast<size_t>(attributes.maxThreadsPerBlock));
    array_size = thrust::min(block_size, (properties.sharedMemPerBlock - attributes.sharedSizeBytes) / sizeof(OutputType));
    smem_bytes = sizeof(OutputType) * array_size;

    // TODO if (shared_array_size < 1) throw cuda exception "insufficient shared memory"

    Closure closure(output.begin(), output.size(), init, output.begin(), binary_op, array_size);

    //std::cout << "Launching " << num_blocks << " blocks of kernel with " << block_size << " threads and " << smem_bytes << " shared memory per block " << std::endl;

    detail::launch_closure(closure, num_blocks, block_size, smem_bytes);
  }

  return output[0];
} // end reduce

} // end reduce_detail

__THRUST_DISABLE_MSVC_POSSIBLE_LOSS_OF_DATA_WARNING_END

template<typename DerivedPolicy,
         typename InputIterator,
         typename OutputType,
         typename BinaryFunction>
__host__ __device__
OutputType reduce(execution_policy<DerivedPolicy> &exec,
                  InputIterator first,
                  InputIterator last,
                  OutputType init,
                  BinaryFunction binary_op)
{
  struct workaround
  {
    __host__ __device__
    static OutputType parallel_path(execution_policy<DerivedPolicy> &exec, InputIterator first, InputIterator last, OutputType init, BinaryFunction binary_op)
    {
      return reduce_detail::reduce(exec, first, last, init, binary_op);
    }

    __host__ __device__
    static OutputType sequential_path(execution_policy<DerivedPolicy> &, InputIterator first, InputIterator last, OutputType init, BinaryFunction binary_op)
    {
      return thrust::reduce(thrust::seq, first, last, init, binary_op);
    }
  };

#if !defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 350)
  return workaround::parallel_path(exec, first, last, init, binary_op);
#else
  return workaround::sequential_path(exec, first, last, init, binary_op);
#endif
} // end reduce()

} // end namespace detail
} // end namespace cuda
} // end namespace system
} // end namespace thrust

<|endoftext|>


<commit_before>#include "scope.h"
#include <yuni/core/noncopyable.h>
#include "details/utils/check-for-valid-identifier-name.h"
#include "details/ast/ast.h"
#include "details/ir/emit.h"

using namespace Yuni;

namespace ny {
namespace ir {
namespace Producer {

namespace {

bool varValueInitialization(Scope& scope, uint32_t& localvar, AST::Node& varAssign,
							AST::Node& varnodeDecl, const AnyString& varname) {
	for (auto& assignChild : varAssign.children) {
		switch (assignChild.rule) {
			case AST::rgExpr: {
				bool success = scope.visitASTExpr(assignChild, localvar);
				if (unlikely(not success)) {
					error(varnodeDecl) << "could not declare '" << varname << "': error in initialization";
					return false;
				}
				break;
			}
			case AST::rgOperatorKind: {
				break; // operator =
			}
			default:
				return unexpectedNode(assignChild, "[var/init]");
		}
	}
	return true;
}

bool generateTypeofForClassVar(Scope& scope, uint32_t& lvid, AST::Node& varAssign) {
	// typeof (+2)
	//   tk-typeof, typeof
	//   call (+3)
	//       tk-parenthese-open, (
	//       call-parameter
	//       |   expr
	//       |     identifier: A
	auto typeofn = make_ref<AST::Node>(AST::rgTypeof);
	auto call = make_ref<AST::Node>(AST::rgCall);
	typeofn->children.push_back(call);
	// first parameter - the expr
	{
		auto param = make_ref<AST::Node>(AST::rgCallParameter);
		call->children.push_back(param);
		uint index = varAssign.findFirst(AST::rgExpr);
		if (unlikely(not (index < varAssign.children.size()))) {
			assert(false and "no node <expr> in <var-assign>");
			return false;
		}
		param->children.push_back(&(varAssign.children[index]));
	}
	return scope.visitASTExprTypeof(*typeofn, lvid);
}

bool generateInitFuncForClassVar(Scope& scope, const AnyString& varname, uint32_t lvid,
								 AST::Node& varAssign) {
	// name of the generated func for initialize the class variable
	ShortString64 funcName;
	funcName << "^default-var-%" << lvid << '-' << varname;
	assert(lvid > 0);
	// generating INIT
	//
	// variable member initialization is merely a call to the intrinsic `__nanyc_fieldset`
	// the first parameter is the result of the default value
	// the second parameter is the register where the name of member has been stored
	if (!scope.context.reuse.func.node)
		scope.context.prepareReuseForVariableMembers();
	scope.context.reuse.func.funcname->text = funcName;
	scope.context.reuse.func.varname->text  = varname;
	// Updating the EXPR
	uint index = varAssign.findFirst(AST::rgExpr);
	if (unlikely(not (index < varAssign.children.size()))) {
		assert(false and "no node <expr> in <var-assign>");
		return false;
	}
	scope.context.reuse.func.callparam->children.push_back(&(varAssign.children[index]));
	bool success = scope.visitASTFunc(*(scope.context.reuse.func.node));
	scope.context.reuse.func.callparam->children.clear();
	return success;
}

bool emitVarInClass(Scope& scope, const AnyString& varname, AST::Node& node, AST::Node* varType,
					AST::Node* varAssign, bool ref, bool constant) {
	auto& irout = scope.ircode();
	if (debugmode) {
		ir::emit::trace(irout);
		ir::emit::trace(irout, [&]() {
			return String{"class var "} << varname;
		});
	}
	// the new member variable
	auto mbvar = scope.nextvar();
	ir::emit::alloc(irout, mbvar);
	// the type of the expression
	uint32_t lvid = 0;
	if (varType != nullptr) {
		if (not scope.visitASTType(*varType, lvid))
			return false;
	}
	else {
		// type definition, via typeof(varAssign)
		if (not generateTypeofForClassVar(scope, lvid, *varAssign))
			return false;
	}
	if (unlikely(0 == lvid))
		return false;
	// follow
	{
		auto& operands    = irout.emit<isa::Op::follow>();
		operands.follower = mbvar;
		operands.lvid     = lvid;
		operands.symlink  = 0;
	}
	// preserve var / ref / const
	ir::emit::type::qualifierRef(irout, mbvar, ref);
	if (constant)
		ir::emit::type::qualifierConst(irout, mbvar, true);
	// variable definition
	scope.emitDebugpos(node);
	ir::emit::blueprint::var(irout, mbvar, varname);
	// generating an INIT func for the variable
	return generateInitFuncForClassVar(scope, varname, mbvar, *varAssign);
}

bool emitVarInFunc(Scope& scope, const AnyString& varname, AST::Node& node, AST::Node* varType,
		AST::Node* varAssign, bool ref, bool constant) {
	auto& irout = scope.ircode();
	// create the variable itself
	uint32_t varlvid = ir::emit::alloc(irout, scope.nextvar());
	if (varType != nullptr) {
		uint32_t lvidtype;
		if (not scope.visitASTType(*varType, lvidtype) or lvidtype == 0)
			return false;
		// follow
		auto& operands    = irout.emit<isa::Op::follow>();
		operands.follower = varlvid;
		operands.lvid     = lvidtype;
		operands.symlink  = 0;
	}
	// preserve var / ref / const
	ir::emit::type::qualifierRef(irout, varlvid, ref);
	if (constant)
		ir::emit::type::qualifierConst(irout, varlvid, true);
	// default value
	{
		uint32_t rhs = 0;
		ir::emit::ScopeLocker opscope{irout};
		if (not varValueInitialization(scope, rhs, *varAssign, node, varname))
			return false;
		assert(rhs != 0);
		if (not ref)
			ir::emit::assign(irout, varlvid, rhs, false);
		else {
			ir::emit::copy(irout, varlvid, rhs); // re-acquire to keep the value alive
			ir::emit::ref(irout, varlvid);
		}
	}
	// important: the alias must be declared *after* the right value
	// (otherwise it may be used by the code)
	scope.emitDebugpos(node); // reset the debug position
	ir::emit::namealias(irout, varlvid, varname);
	return true;
}

bool emitProperty(Scope& scope, const AnyString& varname, AST::Node& node, AST::Node* /*varType*/,
		AST::Node& varAssign, bool ref) {
	AST::Node* nodeGet = nullptr;
	AST::Node* nodeSet = nullptr;
	for (auto& object : varAssign.children) {
		switch (object.rule) {
			case AST::rgObject: {
				for (auto& child : object.children) {
					switch (child.rule) {
						case AST::rgObjectEntry: {
							bool validAST = ((child.children.size() == 2)
											 and child.children[0].rule == AST::rgIdentifier
											 and child.children[1].rule == AST::rgExpr);
							if (unlikely(not validAST))
								return (error(child) << "invalid AST node [property]");
							AnyString name = child.children[0].text;
							if (name == "get") {
								if (unlikely(nodeGet))
									return (error(child) << "property: multiple definition of 'get'");
								nodeGet = &(child.children[1]);
								break;
							}
							if (name == "set") {
								if (unlikely(nodeSet))
									return (error(child) << "property: multiple definition of 'set'");
								nodeSet = &(child.children[1]);
								break;
							}
							return (error(child) << "unsupported property component '" << name << "' (expected: 'get' or 'set')");
						}
						default:
							return unexpectedNode(child, "[property]");
					}
				}
				break;
			}
			case AST::rgExpr: {
				// read-only property
				if (unlikely(nodeGet))
					return (error(object) << "property: multiple definition of 'get'");
				nodeGet = &object;
				break;
			}
			default:
				return unexpectedNode(object, "[property/object]");
		}
	}
	if (unlikely(!nodeGet and !nodeSet))
		return (error(node) << "empty property definition");
	bool success = true;
	ShortString128 propname;
	if (nodeGet) {
		if (!scope.context.reuse.properties.get.node)
			scope.context.prepareReuseForPropertiesGET();
		propname << "^propget^" << varname;
		scope.context.reuse.properties.get.propname->text = propname;
		auto& type = *(scope.context.reuse.properties.get.type);
		type.children.clear();
		if (ref)
			type.children.push_back(scope.context.reuse.properties.get.typeIsRefAny);
		else
			type.children.push_back(scope.context.reuse.properties.get.typeIsAny);
		auto& returnValue = *(scope.context.reuse.properties.get.returnValue);
		returnValue.children.clear();
		returnValue.children.push_back(nodeGet);
		auto& funcnode = *(scope.context.reuse.properties.get.node);
		success &= scope.visitASTFunc(funcnode);
		// to avoid crap printed in the debugger
		scope.context.reuse.properties.get.propname->text.clear();
		type.children.clear();
		returnValue.children.clear();
	}
	if (nodeSet) {
		if (!scope.context.reuse.properties.set.node)
			scope.context.prepareReuseForPropertiesSET();
		propname.clear() << "^propset^" << varname;
		scope.context.reuse.properties.set.propname->text = propname;
		auto& body = *(scope.context.reuse.properties.set.body);
		body.children.clear();
		body.children.push_back(nodeSet);
		auto& funcnode = *(scope.context.reuse.properties.set.node);
		success &= scope.visitASTFunc(funcnode);
		// to avoid crap printed in the debugger
		scope.context.reuse.properties.set.propname->text.clear();
		body.children.clear();
	}
	return success;
}

} // namespace

bool Scope::visitASTVar(AST::Node& node) {
	assert(node.rule == AST::rgVar);
	assert(not node.children.empty());
	AnyString varname;
	bool ref = false;
	bool constant = false;
	AST::Node* varnodeDecl = nullptr;
	AST::Node* varAssign = nullptr;
	AST::Node* varType = nullptr;
	bool isProperty = false;
	for (auto& child : node.children) {
		switch (child.rule) {
			case AST::rgIdentifier: {
				varnodeDecl = &child;
				varname = child.text;
				if (unlikely(not checkForValidIdentifierName(child, varname)))
					return false;
				break;
			}
			case AST::rgVarAssign: {
				varAssign = &child;
				break;
			}
			case AST::rgVarProperty: {
				isProperty = true;
				varAssign = &child;
				break;
			}
			case AST::rgVarType: {
				varType = &child;
				if (child.children.size() == 1 and child.children[0].rule == AST::rgType)
					varType = &(child.children[0]);
				else
					error(child) << "invalid type definition";
				break;
			}
			case AST::rgVarByValue: {
				break; // nothing to do
			}
			case AST::rgConst: {
				constant = true;
				warning(child) << "'const' in var declaration is currently not supported";
				break;
			}
			case AST::rgRef: {
				ref = true;
				break;
			}
			case AST::rgCref: {
				warning(child) << "'const' in var declaration is currently not supported";
				ref = true;
				constant = true;
				break;
			}
			case AST::rgFuncParamVariadic: {
				return (error(child) << "variadic parameter not allowed in variable definition");
			}
			default:
				return unexpectedNode(child, "[var]");
		}
	}
	if (unlikely(varnodeDecl == nullptr))
		return (ice() << "invalid null pointer for the var-decl node");
	if (unlikely(varAssign == nullptr)) // the variable currently must have a default value
		return (error(*varnodeDecl) << "value initialization is missing for '" << varname << '\'');
	if (not isProperty) {
		switch (kind) {
			case Kind::kfunc:
				return emitVarInFunc(*this, varname, *varnodeDecl, varType, varAssign, ref, constant);
			case Kind::kclass:
				return emitVarInClass(*this, varname, *varnodeDecl, varType, varAssign, ref, constant);
			default:
				return (ice(*varnodeDecl) << "var declaration: unsupported scope type");
		}
	}
	else {
		if (unlikely(constant))
			return (error(node) << "'const' keyword for properties is not supported yet");
		if (unlikely(varType))
			return (error(node) << "'type' for properties is not supported yet");
		switch (kind) {
			case Kind::kclass:
			case Kind::undefined:
				return emitProperty(*this, varname, *varnodeDecl, varType, *varAssign, ref);
			case Kind::kfunc:
				return (error(*varnodeDecl) << "properties in functions not implemented");
		}
	}
	return false;
}

} // namespace Producer
} // namespace ir
} // namespace ny
<commit_msg>ast2ir: indentation<commit_after>#include "scope.h"
#include <yuni/core/noncopyable.h>
#include "details/utils/check-for-valid-identifier-name.h"
#include "details/ast/ast.h"
#include "details/ir/emit.h"

using namespace Yuni;

namespace ny {
namespace ir {
namespace Producer {

namespace {

bool varValueInitialization(Scope& scope, uint32_t& localvar, AST::Node& varAssign,
		AST::Node& varnodeDecl, const AnyString& varname) {
	for (auto& assignChild : varAssign.children) {
		switch (assignChild.rule) {
			case AST::rgExpr: {
				bool success = scope.visitASTExpr(assignChild, localvar);
				if (unlikely(not success)) {
					error(varnodeDecl) << "could not declare '" << varname << "': error in initialization";
					return false;
				}
				break;
			}
			case AST::rgOperatorKind: {
				break; // operator =
			}
			default:
				return unexpectedNode(assignChild, "[var/init]");
		}
	}
	return true;
}

bool generateTypeofForClassVar(Scope& scope, uint32_t& lvid, AST::Node& varAssign) {
	// typeof (+2)
	//   tk-typeof, typeof
	//   call (+3)
	//       tk-parenthese-open, (
	//       call-parameter
	//       |   expr
	//       |     identifier: A
	auto typeofn = make_ref<AST::Node>(AST::rgTypeof);
	auto call = make_ref<AST::Node>(AST::rgCall);
	typeofn->children.push_back(call);
	// first parameter - the expr
	{
		auto param = make_ref<AST::Node>(AST::rgCallParameter);
		call->children.push_back(param);
		uint index = varAssign.findFirst(AST::rgExpr);
		if (unlikely(not (index < varAssign.children.size()))) {
			assert(false and "no node <expr> in <var-assign>");
			return false;
		}
		param->children.push_back(&(varAssign.children[index]));
	}
	return scope.visitASTExprTypeof(*typeofn, lvid);
}

bool generateInitFuncForClassVar(Scope& scope, const AnyString& varname, uint32_t lvid,
		AST::Node& varAssign) {
	// name of the generated func for initialize the class variable
	ShortString64 funcName;
	funcName << "^default-var-%" << lvid << '-' << varname;
	assert(lvid > 0);
	// generating INIT
	//
	// variable member initialization is merely a call to the intrinsic `__nanyc_fieldset`
	// the first parameter is the result of the default value
	// the second parameter is the register where the name of member has been stored
	if (!scope.context.reuse.func.node)
		scope.context.prepareReuseForVariableMembers();
	scope.context.reuse.func.funcname->text = funcName;
	scope.context.reuse.func.varname->text  = varname;
	// Updating the EXPR
	uint index = varAssign.findFirst(AST::rgExpr);
	if (unlikely(not (index < varAssign.children.size()))) {
		assert(false and "no node <expr> in <var-assign>");
		return false;
	}
	scope.context.reuse.func.callparam->children.push_back(&(varAssign.children[index]));
	bool success = scope.visitASTFunc(*(scope.context.reuse.func.node));
	scope.context.reuse.func.callparam->children.clear();
	return success;
}

bool emitVarInClass(Scope& scope, const AnyString& varname, AST::Node& node, AST::Node* varType,
					AST::Node* varAssign, bool ref, bool constant) {
	auto& irout = scope.ircode();
	if (debugmode) {
		ir::emit::trace(irout);
		ir::emit::trace(irout, [&]() {
			return String{"class var "} << varname;
		});
	}
	// the new member variable
	auto mbvar = scope.nextvar();
	ir::emit::alloc(irout, mbvar);
	// the type of the expression
	uint32_t lvid = 0;
	if (varType != nullptr) {
		if (not scope.visitASTType(*varType, lvid))
			return false;
	}
	else {
		// type definition, via typeof(varAssign)
		if (not generateTypeofForClassVar(scope, lvid, *varAssign))
			return false;
	}
	if (unlikely(0 == lvid))
		return false;
	// follow
	{
		auto& operands    = irout.emit<isa::Op::follow>();
		operands.follower = mbvar;
		operands.lvid     = lvid;
		operands.symlink  = 0;
	}
	// preserve var / ref / const
	ir::emit::type::qualifierRef(irout, mbvar, ref);
	if (constant)
		ir::emit::type::qualifierConst(irout, mbvar, true);
	// variable definition
	scope.emitDebugpos(node);
	ir::emit::blueprint::var(irout, mbvar, varname);
	// generating an INIT func for the variable
	return generateInitFuncForClassVar(scope, varname, mbvar, *varAssign);
}

bool emitVarInFunc(Scope& scope, const AnyString& varname, AST::Node& node, AST::Node* varType,
		AST::Node* varAssign, bool ref, bool constant) {
	auto& irout = scope.ircode();
	// create the variable itself
	uint32_t varlvid = ir::emit::alloc(irout, scope.nextvar());
	if (varType != nullptr) {
		uint32_t lvidtype;
		if (not scope.visitASTType(*varType, lvidtype) or lvidtype == 0)
			return false;
		// follow
		auto& operands    = irout.emit<isa::Op::follow>();
		operands.follower = varlvid;
		operands.lvid     = lvidtype;
		operands.symlink  = 0;
	}
	// preserve var / ref / const
	ir::emit::type::qualifierRef(irout, varlvid, ref);
	if (constant)
		ir::emit::type::qualifierConst(irout, varlvid, true);
	// default value
	{
		uint32_t rhs = 0;
		ir::emit::ScopeLocker opscope{irout};
		if (not varValueInitialization(scope, rhs, *varAssign, node, varname))
			return false;
		assert(rhs != 0);
		if (not ref)
			ir::emit::assign(irout, varlvid, rhs, false);
		else {
			ir::emit::copy(irout, varlvid, rhs); // re-acquire to keep the value alive
			ir::emit::ref(irout, varlvid);
		}
	}
	// important: the alias must be declared *after* the right value
	// (otherwise it may be used by the code)
	scope.emitDebugpos(node); // reset the debug position
	ir::emit::namealias(irout, varlvid, varname);
	return true;
}

bool emitProperty(Scope& scope, const AnyString& varname, AST::Node& node, AST::Node* /*varType*/,
		AST::Node& varAssign, bool ref) {
	AST::Node* nodeGet = nullptr;
	AST::Node* nodeSet = nullptr;
	for (auto& object : varAssign.children) {
		switch (object.rule) {
			case AST::rgObject: {
				for (auto& child : object.children) {
					switch (child.rule) {
						case AST::rgObjectEntry: {
							bool validAST = ((child.children.size() == 2)
											 and child.children[0].rule == AST::rgIdentifier
											 and child.children[1].rule == AST::rgExpr);
							if (unlikely(not validAST))
								return (error(child) << "invalid AST node [property]");
							AnyString name = child.children[0].text;
							if (name == "get") {
								if (unlikely(nodeGet))
									return (error(child) << "property: multiple definition of 'get'");
								nodeGet = &(child.children[1]);
								break;
							}
							if (name == "set") {
								if (unlikely(nodeSet))
									return (error(child) << "property: multiple definition of 'set'");
								nodeSet = &(child.children[1]);
								break;
							}
							return (error(child) << "unsupported property component '" << name << "' (expected: 'get' or 'set')");
						}
						default:
							return unexpectedNode(child, "[property]");
					}
				}
				break;
			}
			case AST::rgExpr: {
				// read-only property
				if (unlikely(nodeGet))
					return (error(object) << "property: multiple definition of 'get'");
				nodeGet = &object;
				break;
			}
			default:
				return unexpectedNode(object, "[property/object]");
		}
	}
	if (unlikely(!nodeGet and !nodeSet))
		return (error(node) << "empty property definition");
	bool success = true;
	ShortString128 propname;
	if (nodeGet) {
		if (!scope.context.reuse.properties.get.node)
			scope.context.prepareReuseForPropertiesGET();
		propname << "^propget^" << varname;
		scope.context.reuse.properties.get.propname->text = propname;
		auto& type = *(scope.context.reuse.properties.get.type);
		type.children.clear();
		if (ref)
			type.children.push_back(scope.context.reuse.properties.get.typeIsRefAny);
		else
			type.children.push_back(scope.context.reuse.properties.get.typeIsAny);
		auto& returnValue = *(scope.context.reuse.properties.get.returnValue);
		returnValue.children.clear();
		returnValue.children.push_back(nodeGet);
		auto& funcnode = *(scope.context.reuse.properties.get.node);
		success &= scope.visitASTFunc(funcnode);
		// to avoid crap printed in the debugger
		scope.context.reuse.properties.get.propname->text.clear();
		type.children.clear();
		returnValue.children.clear();
	}
	if (nodeSet) {
		if (!scope.context.reuse.properties.set.node)
			scope.context.prepareReuseForPropertiesSET();
		propname.clear() << "^propset^" << varname;
		scope.context.reuse.properties.set.propname->text = propname;
		auto& body = *(scope.context.reuse.properties.set.body);
		body.children.clear();
		body.children.push_back(nodeSet);
		auto& funcnode = *(scope.context.reuse.properties.set.node);
		success &= scope.visitASTFunc(funcnode);
		// to avoid crap printed in the debugger
		scope.context.reuse.properties.set.propname->text.clear();
		body.children.clear();
	}
	return success;
}

} // namespace

bool Scope::visitASTVar(AST::Node& node) {
	assert(node.rule == AST::rgVar);
	assert(not node.children.empty());
	AnyString varname;
	bool ref = false;
	bool constant = false;
	AST::Node* varnodeDecl = nullptr;
	AST::Node* varAssign = nullptr;
	AST::Node* varType = nullptr;
	bool isProperty = false;
	for (auto& child : node.children) {
		switch (child.rule) {
			case AST::rgIdentifier: {
				varnodeDecl = &child;
				varname = child.text;
				if (unlikely(not checkForValidIdentifierName(child, varname)))
					return false;
				break;
			}
			case AST::rgVarAssign: {
				varAssign = &child;
				break;
			}
			case AST::rgVarProperty: {
				isProperty = true;
				varAssign = &child;
				break;
			}
			case AST::rgVarType: {
				varType = &child;
				if (child.children.size() == 1 and child.children[0].rule == AST::rgType)
					varType = &(child.children[0]);
				else
					error(child) << "invalid type definition";
				break;
			}
			case AST::rgVarByValue: {
				break; // nothing to do
			}
			case AST::rgConst: {
				constant = true;
				warning(child) << "'const' in var declaration is currently not supported";
				break;
			}
			case AST::rgRef: {
				ref = true;
				break;
			}
			case AST::rgCref: {
				warning(child) << "'const' in var declaration is currently not supported";
				ref = true;
				constant = true;
				break;
			}
			case AST::rgFuncParamVariadic: {
				return (error(child) << "variadic parameter not allowed in variable definition");
			}
			default:
				return unexpectedNode(child, "[var]");
		}
	}
	if (unlikely(varnodeDecl == nullptr))
		return (ice() << "invalid null pointer for the var-decl node");
	if (unlikely(varAssign == nullptr)) // the variable currently must have a default value
		return (error(*varnodeDecl) << "value initialization is missing for '" << varname << '\'');
	if (not isProperty) {
		switch (kind) {
			case Kind::kfunc:
				return emitVarInFunc(*this, varname, *varnodeDecl, varType, varAssign, ref, constant);
			case Kind::kclass:
				return emitVarInClass(*this, varname, *varnodeDecl, varType, varAssign, ref, constant);
			default:
				return (ice(*varnodeDecl) << "var declaration: unsupported scope type");
		}
	}
	else {
		if (unlikely(constant))
			return (error(node) << "'const' keyword for properties is not supported yet");
		if (unlikely(varType))
			return (error(node) << "'type' for properties is not supported yet");
		switch (kind) {
			case Kind::kclass:
			case Kind::undefined:
				return emitProperty(*this, varname, *varnodeDecl, varType, *varAssign, ref);
			case Kind::kfunc:
				return (error(*varnodeDecl) << "properties in functions not implemented");
		}
	}
	return false;
}

} // namespace Producer
} // namespace ir
} // namespace ny
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: cfgdesc.hxx,v $
 *
 *  $Revision: 1.1.1.1 $
 *
 *  last change: $Author: hr $ $Date: 2000-09-18 17:14:38 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library 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.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _CFGDESC_HXX
#define _CFGDESC_HXX

#ifndef _STRING_HXX //autogen
#include <tools/string.hxx>
#endif

class SwCfgDesc
{
protected:
    String aFuncText;
    String aLibName;
    USHORT nEvent;

    SwCfgDesc(const String &rFuncName, USHORT nId);
    SwCfgDesc(const String &rLib,const String &rMacro);
    ~SwCfgDesc();

public:
    void SetEvent(USHORT nE);               // inline
    void SetFuncText(const String &rStr);   // inline
    void SetMacro(const String &rLib, const String &rMac);
    BOOL IsMacro() const;
    USHORT GetEvent() const;                // inline
    String GetFuncText() const;             // inline
    String GetMacro() const;                // inline
    String GetLib() const;              // inline
};

// INLINE METHODE --------------------------------------------------------
inline void SwCfgDesc::SetEvent(USHORT nE) {
    nEvent = nE;
}

inline void SwCfgDesc::SetFuncText(const String &rStr) {
    aFuncText = rStr;
}

inline  USHORT SwCfgDesc::GetEvent() const { return nEvent; }
inline  String SwCfgDesc::GetFuncText() const { return aFuncText; }
inline  String SwCfgDesc::GetMacro() const { return aFuncText; }
inline  String SwCfgDesc::GetLib() const { return aLibName; }

#endif
<commit_msg>INTEGRATION: CWS ooo19126 (1.1.1.1.1462); FILE MERGED 2005/09/05 13:44:59 rt 1.1.1.1.1462.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: cfgdesc.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 09:02:46 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library 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.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _CFGDESC_HXX
#define _CFGDESC_HXX

#ifndef _STRING_HXX //autogen
#include <tools/string.hxx>
#endif

class SwCfgDesc
{
protected:
    String aFuncText;
    String aLibName;
    USHORT nEvent;

    SwCfgDesc(const String &rFuncName, USHORT nId);
    SwCfgDesc(const String &rLib,const String &rMacro);
    ~SwCfgDesc();

public:
    void SetEvent(USHORT nE);               // inline
    void SetFuncText(const String &rStr);   // inline
    void SetMacro(const String &rLib, const String &rMac);
    BOOL IsMacro() const;
    USHORT GetEvent() const;                // inline
    String GetFuncText() const;             // inline
    String GetMacro() const;                // inline
    String GetLib() const;              // inline
};

// INLINE METHODE --------------------------------------------------------
inline void SwCfgDesc::SetEvent(USHORT nE) {
    nEvent = nE;
}

inline void SwCfgDesc::SetFuncText(const String &rStr) {
    aFuncText = rStr;
}

inline  USHORT SwCfgDesc::GetEvent() const { return nEvent; }
inline  String SwCfgDesc::GetFuncText() const { return aFuncText; }
inline  String SwCfgDesc::GetMacro() const { return aFuncText; }
inline  String SwCfgDesc::GetLib() const { return aLibName; }

#endif
<|endoftext|>


<commit_before>#include "transactionrecord.h"
#include "wallet/wallet.h"
#include "base58.h"

std::string GetTxProject(uint256 hash, int& out_blocknumber, int& out_blocktype, double& out_rac);

/* Return positive answer if transaction should be shown in list. */
bool TransactionRecord::showTransaction(const CWalletTx &wtx, bool datetime_limit_flag, const int64_t &datetime_limit)
{

    // Do not show transactions earlier than the datetime_limit if the flag is set.
    if (datetime_limit_flag && (int64_t) wtx.nTime < datetime_limit)
    {
        return false;
    }

    std::string ShowOrphans = GetArg("-showorphans", "false");

	//R Halford - POS Transactions - If Orphaned follow showorphans directive:
	if (wtx.IsCoinStake() && !wtx.IsInMainChain())
	{
	       //Orphaned tx
		   return (ShowOrphans=="true" ? true : false);
    }

    if (wtx.IsCoinBase())
    {
        // Ensures we show generated coins / mined transactions at depth 1
        if (!wtx.IsInMainChain())
        {
            return false;
        }
    }

    // Suppress OP_RETURN transactions if they did not originate from you.
    // This is not "very" taxing but necessary since the transaction is in the wallet already.
    // We only do this for version 1 transactions, because this legacy error does not occur
    // anymore, and we can't filter entire transactions that have OP_RETURNs, since
    // some outputs are relevent with the new contract types, such as messages.
    if (wtx.nVersion == 1 && !wtx.IsFromMe())
    {
        for (auto const& txout : wtx.vout)
        {
            if (txout.scriptPubKey == (CScript() << OP_RETURN))
                return false;
        }
    }
    return true;
}

/*
 * Decompose CWallet transaction to model transaction records.
 */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
    QList<TransactionRecord> parts;
    int64_t nTime = wtx.GetTxTime();
    int64_t nCredit = wtx.GetCredit(true);
    int64_t nDebit = wtx.GetDebit();
    int64_t nNet = nCredit - nDebit;
    size_t wtx_size = wtx.vout.size();
    uint256 hash = wtx.GetHash();

    std::map<std::string, std::string> mapValue = wtx.mapValue;

    bool fContractPresent = false;
    NN::ContractType ContractType;

    if (!wtx.GetContracts().empty())
    {
         const auto& contract = wtx.GetContracts().begin();
         fContractPresent = true;
         ContractType = contract->m_type.Value();
    }

    // This is legacy CoinBase for PoW, no longer used.
    if (wtx.IsCoinBase())
    {
        for (const auto& txout :wtx.vout)
        {
            if (wallet->IsMine(txout) != ISMINE_NO)
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number

                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    sub.address = CBitcoinAddress(address).ToString();
                }

                // Generated (proof-of-work)
                sub.type = TransactionRecord::Generated;
                sub.credit = txout.nValue;

                parts.append(sub);
            }
        }
    }
    // Since we are now separating out the sent sidestake info
    // into a separate subtransaction, we need to include the entire
    // value of the coinstake transaction here, rather than the previous
    // counting of only IsMine outputs.
    else if (wtx.IsCoinStake())
    {
        // We check the first coinstake output (zero is empty) for IsMine to
        // determine how to characterize the entire coinstake transaction. The
        // sidestakes to other (not mine) addresses are accounted for as negatives
        // in a separate subtransaction. The first output is ALWAYS guaranteed to be
        // the stake return to the original owner, and so matches the input.
        if (wallet->IsMine(wtx.vout[1]) != ISMINE_NO)
        {
            TransactionRecord sub(hash, nTime);
            CTxDestination address;
            sub.idx = parts.size();
            sub.vout = 1;

            sub.type = TransactionRecord::Generated;
            // The coinstake HAS to be from an address.
            if(ExtractDestination(wtx.vout[1].scriptPubKey, address))
            {
                sub.address = CBitcoinAddress(address).ToString();
            }

            // Here we add up all of the outputs, whether they are ours (the stake return with
            // apportioned reward, or not (sidestake), because the part that is not ours
            // will be accounted in the separated sidestake send transaction.
            sub.credit = 0;
            for (const auto& txout : wtx.vout)
            {
                sub.credit += txout.nValue;
            }

            sub.debit = -nDebit;

            // Append the subtransaction to the parts QList (transaction record).
            parts.append(sub);
        }

        // We only want outputs > 1 because the zeroth output is always empty,
        // and the first output is always the staker's. Output 2 onwards may or
        // may not be a sidestake, depending on whether stakesplitting is active,
        // or whether sidestaking is even turned on.
        // There is no coalescing here. A separate subtransaction is created for each
        // sidestake.
        for (unsigned int t = 2; t < wtx_size; t++)
        {
            // If this is not a stake split AND either vout[1] is mine OR
            // vout[t] is mine
            if (wtx.vout[t].scriptPubKey != wtx.vout[1].scriptPubKey &&
                    (wallet->IsMine(wtx.vout[1]) != ISMINE_NO ||
                     wallet->IsMine(wtx.vout[t]) != ISMINE_NO))
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number
                sub.vout = t;

                sub.type = TransactionRecord::Generated;

                if (ExtractDestination(wtx.vout[t].scriptPubKey, address))
                {
                    sub.address = CBitcoinAddress(address).ToString();
                }

                int64_t nValue = wtx.vout[t].nValue;

                if (wallet->IsMine(wtx.vout[t]) != ISMINE_NO)
                {
                    sub.credit = nValue;
                }
                else
                {
                    sub.debit = -nValue;
                }

                parts.append(sub);
            }
        }
    }
    else if (nNet > 0)
    {
        for (const auto& txout : wtx.vout)
        {
            if (wallet->IsMine(txout) != ISMINE_NO)
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number

                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Received by Bitcoin Address
                    sub.type = TransactionRecord::RecvWithAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
                    sub.type = TransactionRecord::RecvFromOther;
                    sub.address = mapValue["from"];
                }

                if (fContractPresent && ContractType == NN::ContractType::MESSAGE)
                {
                    sub.type = TransactionRecord::Message;
                }

                sub.credit = txout.nValue;

                parts.append(sub);
            }
        }
    }
    else // Everything else
    {
        bool fAllFromMe = true;
        for (auto const& txin : wtx.vin)
            fAllFromMe = fAllFromMe && (wallet->IsMine(txin) != ISMINE_NO);

        bool fAllToMe = true;
        for (auto const& txout : wtx.vout)
            fAllToMe = fAllToMe && (wallet->IsMine(txout) != ISMINE_NO);

        if (fAllFromMe && fAllToMe)
        {
            // Payment to self
            int64_t nChange = wtx.GetChange();

            parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
                                           -(nDebit - nChange), nCredit - nChange, 0));
        }
        else if (fAllFromMe)
        {
            //
            // Debit
            //
            int64_t nTxFee = nDebit - wtx.GetValueOut();

            // for tracking message type display
            bool fMessageDisplayed = false;

            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
            {
                const CTxOut& txout = wtx.vout[nOut];
                TransactionRecord sub(hash, nTime);
                sub.idx = parts.size();

                if(wallet->IsMine(txout) != ISMINE_NO)
                {
                    // Ignore parts sent to self, as this is usually the change
                    // from a transaction sent back to our own address.
                    continue;
                }

                CTxDestination address;
                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Sent to Bitcoin Address
                    sub.type = TransactionRecord::SendToAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.type = TransactionRecord::SendToOther;
                    sub.address = mapValue["to"];
                }

                int64_t nValue = txout.nValue;
                /* Add fee to first output */
                if (nTxFee > 0)
                {
                    nValue += nTxFee;
                    nTxFee = 0;
                }
                sub.debit = -nValue;

                // Determine if the transaction is a beacon advertisement or a vote.
                // For right now, there should only be one contract in a transaction.
                // We will simply select the first and only one. Note that we are
                // looping through the outputs one by one in the for loop above this,
                // So if we get here, we are not a coinbase or coinstake, and we are on
                // an output that isn't ours. The worst that can happen from this
                // simple approach is to label more than one output with the
                // first found contract type. For right now, this is sufficient, because
                // the contracts that are sent right now only contain two outputs,
                // the burn and the change. We will have to get more sophisticated
                // when we allow more than one contract per transaction.

                // Notice this doesn't mess with the value or debit, it simply
                // overrides the TransactionRecord enum type.
                if (fContractPresent)
                {
                    switch (ContractType)
                    {
                    case NN::ContractType::BEACON:
                        sub.type = TransactionRecord::BeaconAdvertisement;
                        break;
                    case NN::ContractType::POLL:
                        sub.type = TransactionRecord::Poll;
                        break;
                    case NN::ContractType::VOTE:
                        sub.type = TransactionRecord::Vote;
                        break;
                    case NN::ContractType::MESSAGE:
                        // Only display the message type for the first not is mine output
                        if (!fMessageDisplayed && wallet->IsMine(txout) == ISMINE_NO)
                        {
                            sub.type = TransactionRecord::Message;
                            fMessageDisplayed = true;
                        }
                        // Do not display the op return output for a send message contract separately.
                        else if (txout.scriptPubKey[0] == OP_RETURN)
                        {
                            continue;
                        }
                        break;
                    default:
                        break; // Suppress warning
                    }
                }

                parts.append(sub);
            }
        }
        else
        {
            //
            // Mixed debit transaction, can't break down payees
            //
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0, 0));
        }
    }

    return parts;
}

void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
    AssertLockHeld(cs_main);
    // Determine transaction status

    // Find the block the tx is in
    CBlockIndex* pindex = NULL;
    BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock);
    if (mi != mapBlockIndex.end())
        pindex = (*mi).second;

    // Sort order, unrecorded transactions sort to the top
    status.sortKey = strprintf("%010d-%01d-%010u-%03d",
        (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
        (wtx.IsCoinBase() ? 1 : 0),
        wtx.nTimeReceived,
        idx);
    status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
    status.depth = wtx.GetDepthInMainChain();
    status.cur_num_blocks = nBestHeight;

    if (!IsFinalTx(wtx, nBestHeight + 1))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
        {
            status.status = TransactionStatus::OpenUntilBlock;
            status.open_for = wtx.nLockTime - nBestHeight;
        }
        else
        {
            status.status = TransactionStatus::OpenUntilDate;
            status.open_for = wtx.nLockTime;
        }
    }

    // For generated transactions, determine maturity
    else if(type == TransactionRecord::Generated)
    {
        if (wtx.GetBlocksToMaturity() > 0)
        {
            status.status = TransactionStatus::Immature;

            if (wtx.IsInMainChain())
            {
                status.matures_in = wtx.GetBlocksToMaturity();

                // Check if the block was requested by anyone
                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
                    status.status = TransactionStatus::MaturesWarning;
            }
            else
            {
                status.status = TransactionStatus::NotAccepted;
            }
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
    else
    {
        if (status.depth < 0)
        {
            status.status = TransactionStatus::Conflicted;
        }
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
        {
            status.status = TransactionStatus::Offline;
        }
        else if (status.depth == 0)
        {
            status.status = TransactionStatus::Unconfirmed;
        }
        else if (status.depth < RecommendedNumConfirmations)
        {
            status.status = TransactionStatus::Confirming;
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
}

bool TransactionRecord::statusUpdateNeeded()
{
    AssertLockHeld(cs_main);
    return status.cur_num_blocks != nBestHeight;
}

std::string TransactionRecord::getTxID()
{
    return hash.ToString();
}

<commit_msg>Fix warning of possible uninitialized ContractType<commit_after>#include "transactionrecord.h"
#include "wallet/wallet.h"
#include "base58.h"

std::string GetTxProject(uint256 hash, int& out_blocknumber, int& out_blocktype, double& out_rac);

/* Return positive answer if transaction should be shown in list. */
bool TransactionRecord::showTransaction(const CWalletTx &wtx, bool datetime_limit_flag, const int64_t &datetime_limit)
{

    // Do not show transactions earlier than the datetime_limit if the flag is set.
    if (datetime_limit_flag && (int64_t) wtx.nTime < datetime_limit)
    {
        return false;
    }

    std::string ShowOrphans = GetArg("-showorphans", "false");

	//R Halford - POS Transactions - If Orphaned follow showorphans directive:
	if (wtx.IsCoinStake() && !wtx.IsInMainChain())
	{
	       //Orphaned tx
		   return (ShowOrphans=="true" ? true : false);
    }

    if (wtx.IsCoinBase())
    {
        // Ensures we show generated coins / mined transactions at depth 1
        if (!wtx.IsInMainChain())
        {
            return false;
        }
    }

    // Suppress OP_RETURN transactions if they did not originate from you.
    // This is not "very" taxing but necessary since the transaction is in the wallet already.
    // We only do this for version 1 transactions, because this legacy error does not occur
    // anymore, and we can't filter entire transactions that have OP_RETURNs, since
    // some outputs are relevent with the new contract types, such as messages.
    if (wtx.nVersion == 1 && !wtx.IsFromMe())
    {
        for (auto const& txout : wtx.vout)
        {
            if (txout.scriptPubKey == (CScript() << OP_RETURN))
                return false;
        }
    }
    return true;
}

/*
 * Decompose CWallet transaction to model transaction records.
 */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
    QList<TransactionRecord> parts;
    int64_t nTime = wtx.GetTxTime();
    int64_t nCredit = wtx.GetCredit(true);
    int64_t nDebit = wtx.GetDebit();
    int64_t nNet = nCredit - nDebit;
    size_t wtx_size = wtx.vout.size();
    uint256 hash = wtx.GetHash();

    std::map<std::string, std::string> mapValue = wtx.mapValue;

    bool fContractPresent = false;
    // Initialize to unknown to prevent a possible uninitialized warning.
    NN::ContractType ContractType = NN::ContractType::UNKNOWN;

    if (!wtx.GetContracts().empty())
    {
         const auto& contract = wtx.GetContracts().begin();
         fContractPresent = true;
         ContractType = contract->m_type.Value();
    }

    // This is legacy CoinBase for PoW, no longer used.
    if (wtx.IsCoinBase())
    {
        for (const auto& txout :wtx.vout)
        {
            if (wallet->IsMine(txout) != ISMINE_NO)
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number

                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    sub.address = CBitcoinAddress(address).ToString();
                }

                // Generated (proof-of-work)
                sub.type = TransactionRecord::Generated;
                sub.credit = txout.nValue;

                parts.append(sub);
            }
        }
    }
    // Since we are now separating out the sent sidestake info
    // into a separate subtransaction, we need to include the entire
    // value of the coinstake transaction here, rather than the previous
    // counting of only IsMine outputs.
    else if (wtx.IsCoinStake())
    {
        // We check the first coinstake output (zero is empty) for IsMine to
        // determine how to characterize the entire coinstake transaction. The
        // sidestakes to other (not mine) addresses are accounted for as negatives
        // in a separate subtransaction. The first output is ALWAYS guaranteed to be
        // the stake return to the original owner, and so matches the input.
        if (wallet->IsMine(wtx.vout[1]) != ISMINE_NO)
        {
            TransactionRecord sub(hash, nTime);
            CTxDestination address;
            sub.idx = parts.size();
            sub.vout = 1;

            sub.type = TransactionRecord::Generated;
            // The coinstake HAS to be from an address.
            if(ExtractDestination(wtx.vout[1].scriptPubKey, address))
            {
                sub.address = CBitcoinAddress(address).ToString();
            }

            // Here we add up all of the outputs, whether they are ours (the stake return with
            // apportioned reward, or not (sidestake), because the part that is not ours
            // will be accounted in the separated sidestake send transaction.
            sub.credit = 0;
            for (const auto& txout : wtx.vout)
            {
                sub.credit += txout.nValue;
            }

            sub.debit = -nDebit;

            // Append the subtransaction to the parts QList (transaction record).
            parts.append(sub);
        }

        // We only want outputs > 1 because the zeroth output is always empty,
        // and the first output is always the staker's. Output 2 onwards may or
        // may not be a sidestake, depending on whether stakesplitting is active,
        // or whether sidestaking is even turned on.
        // There is no coalescing here. A separate subtransaction is created for each
        // sidestake.
        for (unsigned int t = 2; t < wtx_size; t++)
        {
            // If this is not a stake split AND either vout[1] is mine OR
            // vout[t] is mine
            if (wtx.vout[t].scriptPubKey != wtx.vout[1].scriptPubKey &&
                    (wallet->IsMine(wtx.vout[1]) != ISMINE_NO ||
                     wallet->IsMine(wtx.vout[t]) != ISMINE_NO))
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number
                sub.vout = t;

                sub.type = TransactionRecord::Generated;

                if (ExtractDestination(wtx.vout[t].scriptPubKey, address))
                {
                    sub.address = CBitcoinAddress(address).ToString();
                }

                int64_t nValue = wtx.vout[t].nValue;

                if (wallet->IsMine(wtx.vout[t]) != ISMINE_NO)
                {
                    sub.credit = nValue;
                }
                else
                {
                    sub.debit = -nValue;
                }

                parts.append(sub);
            }
        }
    }
    else if (nNet > 0)
    {
        for (const auto& txout : wtx.vout)
        {
            if (wallet->IsMine(txout) != ISMINE_NO)
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number

                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Received by Bitcoin Address
                    sub.type = TransactionRecord::RecvWithAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
                    sub.type = TransactionRecord::RecvFromOther;
                    sub.address = mapValue["from"];
                }

                if (fContractPresent && ContractType == NN::ContractType::MESSAGE)
                {
                    sub.type = TransactionRecord::Message;
                }

                sub.credit = txout.nValue;

                parts.append(sub);
            }
        }
    }
    else // Everything else
    {
        bool fAllFromMe = true;
        for (auto const& txin : wtx.vin)
            fAllFromMe = fAllFromMe && (wallet->IsMine(txin) != ISMINE_NO);

        bool fAllToMe = true;
        for (auto const& txout : wtx.vout)
            fAllToMe = fAllToMe && (wallet->IsMine(txout) != ISMINE_NO);

        if (fAllFromMe && fAllToMe)
        {
            // Payment to self
            int64_t nChange = wtx.GetChange();

            parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
                                           -(nDebit - nChange), nCredit - nChange, 0));
        }
        else if (fAllFromMe)
        {
            //
            // Debit
            //
            int64_t nTxFee = nDebit - wtx.GetValueOut();

            // for tracking message type display
            bool fMessageDisplayed = false;

            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
            {
                const CTxOut& txout = wtx.vout[nOut];
                TransactionRecord sub(hash, nTime);
                sub.idx = parts.size();

                if(wallet->IsMine(txout) != ISMINE_NO)
                {
                    // Ignore parts sent to self, as this is usually the change
                    // from a transaction sent back to our own address.
                    continue;
                }

                CTxDestination address;
                if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Sent to Bitcoin Address
                    sub.type = TransactionRecord::SendToAddress;
                    sub.address = CBitcoinAddress(address).ToString();
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.type = TransactionRecord::SendToOther;
                    sub.address = mapValue["to"];
                }

                int64_t nValue = txout.nValue;
                /* Add fee to first output */
                if (nTxFee > 0)
                {
                    nValue += nTxFee;
                    nTxFee = 0;
                }
                sub.debit = -nValue;

                // Determine if the transaction is a beacon advertisement or a vote.
                // For right now, there should only be one contract in a transaction.
                // We will simply select the first and only one. Note that we are
                // looping through the outputs one by one in the for loop above this,
                // So if we get here, we are not a coinbase or coinstake, and we are on
                // an output that isn't ours. The worst that can happen from this
                // simple approach is to label more than one output with the
                // first found contract type. For right now, this is sufficient, because
                // the contracts that are sent right now only contain two outputs,
                // the burn and the change. We will have to get more sophisticated
                // when we allow more than one contract per transaction.

                // Notice this doesn't mess with the value or debit, it simply
                // overrides the TransactionRecord enum type.
                if (fContractPresent)
                {
                    switch (ContractType)
                    {
                    case NN::ContractType::BEACON:
                        sub.type = TransactionRecord::BeaconAdvertisement;
                        break;
                    case NN::ContractType::POLL:
                        sub.type = TransactionRecord::Poll;
                        break;
                    case NN::ContractType::VOTE:
                        sub.type = TransactionRecord::Vote;
                        break;
                    case NN::ContractType::MESSAGE:
                        // Only display the message type for the first not is mine output
                        if (!fMessageDisplayed && wallet->IsMine(txout) == ISMINE_NO)
                        {
                            sub.type = TransactionRecord::Message;
                            fMessageDisplayed = true;
                        }
                        // Do not display the op return output for a send message contract separately.
                        else if (txout.scriptPubKey[0] == OP_RETURN)
                        {
                            continue;
                        }
                        break;
                    default:
                        break; // Suppress warning
                    }
                }

                parts.append(sub);
            }
        }
        else
        {
            //
            // Mixed debit transaction, can't break down payees
            //
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0, 0));
        }
    }

    return parts;
}

void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
    AssertLockHeld(cs_main);
    // Determine transaction status

    // Find the block the tx is in
    CBlockIndex* pindex = NULL;
    BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock);
    if (mi != mapBlockIndex.end())
        pindex = (*mi).second;

    // Sort order, unrecorded transactions sort to the top
    status.sortKey = strprintf("%010d-%01d-%010u-%03d",
        (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
        (wtx.IsCoinBase() ? 1 : 0),
        wtx.nTimeReceived,
        idx);
    status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
    status.depth = wtx.GetDepthInMainChain();
    status.cur_num_blocks = nBestHeight;

    if (!IsFinalTx(wtx, nBestHeight + 1))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
        {
            status.status = TransactionStatus::OpenUntilBlock;
            status.open_for = wtx.nLockTime - nBestHeight;
        }
        else
        {
            status.status = TransactionStatus::OpenUntilDate;
            status.open_for = wtx.nLockTime;
        }
    }

    // For generated transactions, determine maturity
    else if(type == TransactionRecord::Generated)
    {
        if (wtx.GetBlocksToMaturity() > 0)
        {
            status.status = TransactionStatus::Immature;

            if (wtx.IsInMainChain())
            {
                status.matures_in = wtx.GetBlocksToMaturity();

                // Check if the block was requested by anyone
                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
                    status.status = TransactionStatus::MaturesWarning;
            }
            else
            {
                status.status = TransactionStatus::NotAccepted;
            }
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
    else
    {
        if (status.depth < 0)
        {
            status.status = TransactionStatus::Conflicted;
        }
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
        {
            status.status = TransactionStatus::Offline;
        }
        else if (status.depth == 0)
        {
            status.status = TransactionStatus::Unconfirmed;
        }
        else if (status.depth < RecommendedNumConfirmations)
        {
            status.status = TransactionStatus::Confirming;
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
}

bool TransactionRecord::statusUpdateNeeded()
{
    AssertLockHeld(cs_main);
    return status.cur_num_blocks != nBestHeight;
}

std::string TransactionRecord::getTxID()
{
    return hash.ToString();
}

<|endoftext|>