<commit_before>/*
 * Copyright (c) 2014, 2015, 2016, Charles River Analytics, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "robot_localization/filter_base.h"
#include "robot_localization/filter_common.h"

#include <sstream>
#include <iomanip>
#include <limits>
#include <iostream>

namespace RobotLocalization
{
  FilterBase::FilterBase() :
    accelerationLimits_(TWIST_SIZE, 0.0),
    decelerationLimits_(TWIST_SIZE, 0.0),
    controlAcceleration_(TWIST_SIZE),
    controlTimeout_(0.0),
    controlUpdateVector_(TWIST_SIZE, 0),
    latestControlTime_(0.0),
    state_(STATE_SIZE),
    predictedState_(STATE_SIZE),
    transferFunction_(STATE_SIZE, STATE_SIZE),
    transferFunctionJacobian_(STATE_SIZE, STATE_SIZE),
    estimateErrorCovariance_(STATE_SIZE, STATE_SIZE),
    covarianceEpsilon_(STATE_SIZE, STATE_SIZE),
    processNoiseCovariance_(STATE_SIZE, STATE_SIZE),
    identity_(STATE_SIZE, STATE_SIZE),
    debug_(false),
    debugStream_(NULL),
    useControl_(false)
  {
    initialized_ = false;

    // Clear the state and predicted state
    state_.setZero();
    predictedState_.setZero();
    controlAcceleration_.setZero();

    // Prepare the invariant parts of the transfer
    // function
    transferFunction_.setIdentity();

    // Clear the Jacobian
    transferFunctionJacobian_.setZero();

    // Set the estimate error covariance. We want our measurements
    // to be accepted rapidly when the filter starts, so we should
    // initialize the state's covariance with large values.
    estimateErrorCovariance_.setIdentity();
    estimateErrorCovariance_ *= 1e-9;

    // We need the identity for the update equations
    identity_.setIdentity();

    // Set the epsilon matrix to be a matrix with small values on the diagonal
    // It is used to maintain the positive-definite property of the covariance
    covarianceEpsilon_.setIdentity();
    covarianceEpsilon_ *= 0.001;

    // Assume 30Hz from sensor data (configurable)
    sensorTimeout_ = 0.033333333;

    // Initialize our last update and measurement times
    lastUpdateTime_ = 0;
    lastMeasurementTime_ = 0;

    // These can be overridden via the launch parameters,
    // but we need default values.
    processNoiseCovariance_.setZero();
    processNoiseCovariance_(StateMemberX, StateMemberX) = 0.05;
    processNoiseCovariance_(StateMemberY, StateMemberY) = 0.05;
    processNoiseCovariance_(StateMemberZ, StateMemberZ) = 0.06;
    processNoiseCovariance_(StateMemberRoll, StateMemberRoll) = 0.03;
    processNoiseCovariance_(StateMemberPitch, StateMemberPitch) = 0.03;
    processNoiseCovariance_(StateMemberYaw, StateMemberYaw) = 0.06;
    processNoiseCovariance_(StateMemberVx, StateMemberVx) = 0.025;
    processNoiseCovariance_(StateMemberVy, StateMemberVy) = 0.025;
    processNoiseCovariance_(StateMemberVz, StateMemberVz) = 0.04;
    processNoiseCovariance_(StateMemberVroll, StateMemberVroll) = 0.01;
    processNoiseCovariance_(StateMemberVpitch, StateMemberVpitch) = 0.01;
    processNoiseCovariance_(StateMemberVyaw, StateMemberVyaw) = 0.02;
    processNoiseCovariance_(StateMemberAx, StateMemberAx) = 0.01;
    processNoiseCovariance_(StateMemberAy, StateMemberAy) = 0.01;
    processNoiseCovariance_(StateMemberAz, StateMemberAz) = 0.015;
  }

  FilterBase::~FilterBase()
  {
  }

  bool FilterBase::getDebug()
  {
    return debug_;
  }

  const Eigen::MatrixXd& FilterBase::getEstimateErrorCovariance()
  {
    return estimateErrorCovariance_;
  }

  bool FilterBase::getInitializedStatus()
  {
    return initialized_;
  }

  double FilterBase::getLastMeasurementTime()
  {
    return lastMeasurementTime_;
  }

  double FilterBase::getLastUpdateTime()
  {
    return lastUpdateTime_;
  }

  const Eigen::VectorXd& FilterBase::getPredictedState()
  {
    return predictedState_;
  }

  const Eigen::MatrixXd& FilterBase::getProcessNoiseCovariance()
  {
    return processNoiseCovariance_;
  }

  double FilterBase::getSensorTimeout()
  {
    return sensorTimeout_;
  }

  const Eigen::VectorXd& FilterBase::getState()
  {
    return state_;
  }

  void FilterBase::processMeasurement(const Measurement &measurement)
  {
    FB_DEBUG("------ FilterBase::processMeasurement (" << measurement.topicName_ << ") ------\n");

    double delta = 0.0;

    // If we've had a previous reading, then go through the predict/update
    // cycle. Otherwise, set our state and covariance to whatever we get
    // from this measurement.
    if (initialized_)
    {
      // Determine how much time has passed since our last measurement
      delta = measurement.time_ - lastMeasurementTime_;

      FB_DEBUG("Filter is already initialized. Carrying out predict/correct loop...\n"
               "Measurement time is " << std::setprecision(20) << measurement.time_ <<
               ", last measurement time is " << lastMeasurementTime_ << ", delta is " << delta << "\n");

      // Only want to carry out a prediction if it's
      // forward in time. Otherwise, just correct.
      if (delta > 0)
      {
        validateDelta(delta);
        predict(measurement.time_, delta);

        // Return this to the user
        predictedState_ = state_;
      }

      correct(measurement);
    }
    else
    {
      FB_DEBUG("First measurement. Initializing filter.\n");

      // Initialize the filter, but only with the values we're using
      size_t measurementLength = measurement.updateVector_.size();
      for (size_t i = 0; i < measurementLength; ++i)
      {
        state_[i] = (measurement.updateVector_[i] ? measurement.measurement_[i] : state_[i]);
      }

      // Same for covariance
      for (size_t i = 0; i < measurementLength; ++i)
      {
        for (size_t j = 0; j < measurementLength; ++j)
        {
          estimateErrorCovariance_(i, j) = (measurement.updateVector_[i] && measurement.updateVector_[j] ?
                                            measurement.covariance_(i, j) :
                                            estimateErrorCovariance_(i, j));
        }
      }

      initialized_ = true;
    }

    if (delta >= 0.0)
    {
      // Update the last measurement and update time.
      // The measurement time is based on the time stamp of the
      // measurement, whereas the update time is based on this
      // node's current ROS time. The update time is used to
      // determine if we have a sensor timeout, whereas the
      // measurement time is used to calculate time deltas for
      // prediction and correction.
      lastMeasurementTime_ = measurement.time_;
    }

    FB_DEBUG("------ /FilterBase::processMeasurement (" << measurement.topicName_ << ") ------\n");
  }

  void FilterBase::setControl(const Eigen::VectorXd &control, const double controlTime)
  {
    latestControl_ = control;
    latestControlTime_ = controlTime;
  }

  void FilterBase::setControlParams(const std::vector<int> &updateVector, const double controlTimeout,
    const std::vector<double> &accelerationLimits, const std::vector<double> &decelerationLimits)
  {
    useControl_ = true;
    controlUpdateVector_ = updateVector;
    controlTimeout_ = controlTimeout;
    accelerationLimits_ = accelerationLimits;
    decelerationLimits_ = decelerationLimits;
  }

  void FilterBase::setDebug(const bool debug, std::ostream *outStream)
  {
    if (debug)
    {
      if (outStream != NULL)
      {
        debugStream_ = outStream;
        debug_ = true;
      }
      else
      {
        debug_ = false;
      }
    }
    else
    {
      debug_ = false;
    }
  }

  void FilterBase::setEstimateErrorCovariance(const Eigen::MatrixXd &estimateErrorCovariance)
  {
    estimateErrorCovariance_ = estimateErrorCovariance;
  }

  void FilterBase::setLastMeasurementTime(const double lastMeasurementTime)
  {
    lastMeasurementTime_ = lastMeasurementTime;
  }

  void FilterBase::setLastUpdateTime(const double lastUpdateTime)
  {
    lastUpdateTime_ = lastUpdateTime;
  }

  void FilterBase::setProcessNoiseCovariance(const Eigen::MatrixXd &processNoiseCovariance)
  {
    processNoiseCovariance_ = processNoiseCovariance;
  }

  void FilterBase::setSensorTimeout(const double sensorTimeout)
  {
    sensorTimeout_ = sensorTimeout;
  }

  void FilterBase::setState(const Eigen::VectorXd &state)
  {
    state_ = state;
  }

  void FilterBase::validateDelta(double &delta)
  {
    // This handles issues with ROS time when use_sim_time is on and we're playing from bags.
    if (delta > 100000.0)
    {
      FB_DEBUG("Delta was very large. Suspect playing from bag file. Setting to 0.01\n");

      delta = 0.01;
    }
  }

  void FilterBase::prepareControl(const double referenceTime, const double predictionDelta)
  {
    controlAcceleration_.setZero();

    if (useControl_)
    {
      bool timedOut = ::fabs(referenceTime - latestControlTime_) >= controlTimeout_;

      if(timedOut)
      {
        FB_DEBUG("Control timed out. Reference time was " << referenceTime << ", latest control time was " <<
          latestControlTime_ << ", control timeout was " << controlTimeout_ << "\n");
      }

      double frequency = ::fabs(1.0 / predictionDelta);

      if(controlUpdateVector_[ControlMemberVx])
      {
        controlAcceleration_(ControlMemberVx) = computeControlAcceleration(state_(StateMemberVx),
          (timedOut ? 0.0 : latestControl_(ControlMemberVx)), frequency, accelerationLimits_[ControlMemberVx],
          decelerationLimits_[ControlMemberVx]);
      }
      if(controlUpdateVector_[ControlMemberVy])
      {
        controlAcceleration_(ControlMemberVy) = computeControlAcceleration(state_(StateMemberVy),
          (timedOut ? 0.0 : latestControl_(ControlMemberVy)), frequency, accelerationLimits_[ControlMemberVy],
          decelerationLimits_[ControlMemberVy]);
      }
      if(controlUpdateVector_[ControlMemberVz])
      {
        controlAcceleration_(ControlMemberVz) = computeControlAcceleration(state_(StateMemberVz),
          (timedOut ? 0.0 : latestControl_(ControlMemberVz)), frequency, accelerationLimits_[ControlMemberVz],
          decelerationLimits_[ControlMemberVz]);
      }
      if(controlUpdateVector_[ControlMemberVroll])
      {
        controlAcceleration_(ControlMemberVroll) = computeControlAcceleration(state_(StateMemberVroll),
          (timedOut ? 0.0 : latestControl_(ControlMemberVroll)), frequency, accelerationLimits_[ControlMemberVroll],
          decelerationLimits_[ControlMemberVroll]);
      }
      if(controlUpdateVector_[ControlMemberVpitch])
      {
        controlAcceleration_(ControlMemberVpitch) = computeControlAcceleration(state_(StateMemberVpitch),
          (timedOut ? 0.0 : latestControl_(ControlMemberVpitch)), frequency, accelerationLimits_[ControlMemberVpitch],
          decelerationLimits_[ControlMemberVpitch]);
      }
      if(controlUpdateVector_[ControlMemberVyaw])
      {
        controlAcceleration_(ControlMemberVyaw) = computeControlAcceleration(state_(StateMemberVyaw),
          (timedOut ? 0.0 : latestControl_(ControlMemberVyaw)), frequency, accelerationLimits_[ControlMemberVyaw],
          decelerationLimits_[ControlMemberVyaw]);
      }
    }
  }

  void FilterBase::wrapStateAngles()
  {
    state_(StateMemberRoll)  = FilterUtilities::clampRotation(state_(StateMemberRoll));
    state_(StateMemberPitch) = FilterUtilities::clampRotation(state_(StateMemberPitch));
    state_(StateMemberYaw)   = FilterUtilities::clampRotation(state_(StateMemberYaw));
  }

  bool FilterBase::checkMahalanobisThreshold(const Eigen::VectorXd &innovation,
                                             const Eigen::MatrixXd &invCovariance,
                                             const double nsigmas)
  {
    double sqMahalanobis = innovation.dot(invCovariance * innovation);
    double threshold = nsigmas * nsigmas;

    if (sqMahalanobis >= threshold)
    {
      FB_DEBUG("Innovation mahalanobis distance test failed. Squared Mahalanobis is: " << sqMahalanobis << "\n" <<
               "Threshold is: " << threshold << "\n" <<
               "Innovation is: " << innovation << "\n" <<
               "Innovation covariance is:\n" << invCovariance << "\n");

      return false;
    }

    return true;
  }
}  // namespace RobotLocalization
<commit_msg>Cleaning up prepareControl<commit_after>/*
 * Copyright (c) 2014, 2015, 2016, Charles River Analytics, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "robot_localization/filter_base.h"
#include "robot_localization/filter_common.h"

#include <sstream>
#include <iomanip>
#include <limits>
#include <iostream>

namespace RobotLocalization
{
  FilterBase::FilterBase() :
    accelerationLimits_(TWIST_SIZE, 0.0),
    decelerationLimits_(TWIST_SIZE, 0.0),
    controlAcceleration_(TWIST_SIZE),
    controlTimeout_(0.0),
    controlUpdateVector_(TWIST_SIZE, 0),
    latestControlTime_(0.0),
    state_(STATE_SIZE),
    predictedState_(STATE_SIZE),
    transferFunction_(STATE_SIZE, STATE_SIZE),
    transferFunctionJacobian_(STATE_SIZE, STATE_SIZE),
    estimateErrorCovariance_(STATE_SIZE, STATE_SIZE),
    covarianceEpsilon_(STATE_SIZE, STATE_SIZE),
    processNoiseCovariance_(STATE_SIZE, STATE_SIZE),
    identity_(STATE_SIZE, STATE_SIZE),
    debug_(false),
    debugStream_(NULL),
    useControl_(false)
  {
    initialized_ = false;

    // Clear the state and predicted state
    state_.setZero();
    predictedState_.setZero();
    controlAcceleration_.setZero();

    // Prepare the invariant parts of the transfer
    // function
    transferFunction_.setIdentity();

    // Clear the Jacobian
    transferFunctionJacobian_.setZero();

    // Set the estimate error covariance. We want our measurements
    // to be accepted rapidly when the filter starts, so we should
    // initialize the state's covariance with large values.
    estimateErrorCovariance_.setIdentity();
    estimateErrorCovariance_ *= 1e-9;

    // We need the identity for the update equations
    identity_.setIdentity();

    // Set the epsilon matrix to be a matrix with small values on the diagonal
    // It is used to maintain the positive-definite property of the covariance
    covarianceEpsilon_.setIdentity();
    covarianceEpsilon_ *= 0.001;

    // Assume 30Hz from sensor data (configurable)
    sensorTimeout_ = 0.033333333;

    // Initialize our last update and measurement times
    lastUpdateTime_ = 0;
    lastMeasurementTime_ = 0;

    // These can be overridden via the launch parameters,
    // but we need default values.
    processNoiseCovariance_.setZero();
    processNoiseCovariance_(StateMemberX, StateMemberX) = 0.05;
    processNoiseCovariance_(StateMemberY, StateMemberY) = 0.05;
    processNoiseCovariance_(StateMemberZ, StateMemberZ) = 0.06;
    processNoiseCovariance_(StateMemberRoll, StateMemberRoll) = 0.03;
    processNoiseCovariance_(StateMemberPitch, StateMemberPitch) = 0.03;
    processNoiseCovariance_(StateMemberYaw, StateMemberYaw) = 0.06;
    processNoiseCovariance_(StateMemberVx, StateMemberVx) = 0.025;
    processNoiseCovariance_(StateMemberVy, StateMemberVy) = 0.025;
    processNoiseCovariance_(StateMemberVz, StateMemberVz) = 0.04;
    processNoiseCovariance_(StateMemberVroll, StateMemberVroll) = 0.01;
    processNoiseCovariance_(StateMemberVpitch, StateMemberVpitch) = 0.01;
    processNoiseCovariance_(StateMemberVyaw, StateMemberVyaw) = 0.02;
    processNoiseCovariance_(StateMemberAx, StateMemberAx) = 0.01;
    processNoiseCovariance_(StateMemberAy, StateMemberAy) = 0.01;
    processNoiseCovariance_(StateMemberAz, StateMemberAz) = 0.015;
  }

  FilterBase::~FilterBase()
  {
  }

  bool FilterBase::getDebug()
  {
    return debug_;
  }

  const Eigen::MatrixXd& FilterBase::getEstimateErrorCovariance()
  {
    return estimateErrorCovariance_;
  }

  bool FilterBase::getInitializedStatus()
  {
    return initialized_;
  }

  double FilterBase::getLastMeasurementTime()
  {
    return lastMeasurementTime_;
  }

  double FilterBase::getLastUpdateTime()
  {
    return lastUpdateTime_;
  }

  const Eigen::VectorXd& FilterBase::getPredictedState()
  {
    return predictedState_;
  }

  const Eigen::MatrixXd& FilterBase::getProcessNoiseCovariance()
  {
    return processNoiseCovariance_;
  }

  double FilterBase::getSensorTimeout()
  {
    return sensorTimeout_;
  }

  const Eigen::VectorXd& FilterBase::getState()
  {
    return state_;
  }

  void FilterBase::processMeasurement(const Measurement &measurement)
  {
    FB_DEBUG("------ FilterBase::processMeasurement (" << measurement.topicName_ << ") ------\n");

    double delta = 0.0;

    // If we've had a previous reading, then go through the predict/update
    // cycle. Otherwise, set our state and covariance to whatever we get
    // from this measurement.
    if (initialized_)
    {
      // Determine how much time has passed since our last measurement
      delta = measurement.time_ - lastMeasurementTime_;

      FB_DEBUG("Filter is already initialized. Carrying out predict/correct loop...\n"
               "Measurement time is " << std::setprecision(20) << measurement.time_ <<
               ", last measurement time is " << lastMeasurementTime_ << ", delta is " << delta << "\n");

      // Only want to carry out a prediction if it's
      // forward in time. Otherwise, just correct.
      if (delta > 0)
      {
        validateDelta(delta);
        predict(measurement.time_, delta);

        // Return this to the user
        predictedState_ = state_;
      }

      correct(measurement);
    }
    else
    {
      FB_DEBUG("First measurement. Initializing filter.\n");

      // Initialize the filter, but only with the values we're using
      size_t measurementLength = measurement.updateVector_.size();
      for (size_t i = 0; i < measurementLength; ++i)
      {
        state_[i] = (measurement.updateVector_[i] ? measurement.measurement_[i] : state_[i]);
      }

      // Same for covariance
      for (size_t i = 0; i < measurementLength; ++i)
      {
        for (size_t j = 0; j < measurementLength; ++j)
        {
          estimateErrorCovariance_(i, j) = (measurement.updateVector_[i] && measurement.updateVector_[j] ?
                                            measurement.covariance_(i, j) :
                                            estimateErrorCovariance_(i, j));
        }
      }

      initialized_ = true;
    }

    if (delta >= 0.0)
    {
      // Update the last measurement and update time.
      // The measurement time is based on the time stamp of the
      // measurement, whereas the update time is based on this
      // node's current ROS time. The update time is used to
      // determine if we have a sensor timeout, whereas the
      // measurement time is used to calculate time deltas for
      // prediction and correction.
      lastMeasurementTime_ = measurement.time_;
    }

    FB_DEBUG("------ /FilterBase::processMeasurement (" << measurement.topicName_ << ") ------\n");
  }

  void FilterBase::setControl(const Eigen::VectorXd &control, const double controlTime)
  {
    latestControl_ = control;
    latestControlTime_ = controlTime;
  }

  void FilterBase::setControlParams(const std::vector<int> &updateVector, const double controlTimeout,
    const std::vector<double> &accelerationLimits, const std::vector<double> &decelerationLimits)
  {
    useControl_ = true;
    controlUpdateVector_ = updateVector;
    controlTimeout_ = controlTimeout;
    accelerationLimits_ = accelerationLimits;
    decelerationLimits_ = decelerationLimits;
  }

  void FilterBase::setDebug(const bool debug, std::ostream *outStream)
  {
    if (debug)
    {
      if (outStream != NULL)
      {
        debugStream_ = outStream;
        debug_ = true;
      }
      else
      {
        debug_ = false;
      }
    }
    else
    {
      debug_ = false;
    }
  }

  void FilterBase::setEstimateErrorCovariance(const Eigen::MatrixXd &estimateErrorCovariance)
  {
    estimateErrorCovariance_ = estimateErrorCovariance;
  }

  void FilterBase::setLastMeasurementTime(const double lastMeasurementTime)
  {
    lastMeasurementTime_ = lastMeasurementTime;
  }

  void FilterBase::setLastUpdateTime(const double lastUpdateTime)
  {
    lastUpdateTime_ = lastUpdateTime;
  }

  void FilterBase::setProcessNoiseCovariance(const Eigen::MatrixXd &processNoiseCovariance)
  {
    processNoiseCovariance_ = processNoiseCovariance;
  }

  void FilterBase::setSensorTimeout(const double sensorTimeout)
  {
    sensorTimeout_ = sensorTimeout;
  }

  void FilterBase::setState(const Eigen::VectorXd &state)
  {
    state_ = state;
  }

  void FilterBase::validateDelta(double &delta)
  {
    // This handles issues with ROS time when use_sim_time is on and we're playing from bags.
    if (delta > 100000.0)
    {
      FB_DEBUG("Delta was very large. Suspect playing from bag file. Setting to 0.01\n");

      delta = 0.01;
    }
  }

  void FilterBase::prepareControl(const double referenceTime, const double predictionDelta)
  {
    controlAcceleration_.setZero();

    if (useControl_)
    {
      bool timedOut = ::fabs(referenceTime - latestControlTime_) >= controlTimeout_;

      if(timedOut)
      {
        FB_DEBUG("Control timed out. Reference time was " << referenceTime << ", latest control time was " <<
          latestControlTime_ << ", control timeout was " << controlTimeout_ << "\n");
      }

      double frequency = ::fabs(1.0 / predictionDelta);

      for(size_t controlInd = 0; controlInd < TWIST_SIZE; ++controlInd)
      {
        if(controlUpdateVector_[controlInd])
        {
          controlAcceleration_(controlInd) = computeControlAcceleration(state_(controlInd + POSITION_V_OFFSET),
            (timedOut ? 0.0 : latestControl_(controlInd)), frequency, accelerationLimits_[controlInd],
            decelerationLimits_[controlInd]);
        }
      }
    }
  }

  void FilterBase::wrapStateAngles()
  {
    state_(StateMemberRoll)  = FilterUtilities::clampRotation(state_(StateMemberRoll));
    state_(StateMemberPitch) = FilterUtilities::clampRotation(state_(StateMemberPitch));
    state_(StateMemberYaw)   = FilterUtilities::clampRotation(state_(StateMemberYaw));
  }

  bool FilterBase::checkMahalanobisThreshold(const Eigen::VectorXd &innovation,
                                             const Eigen::MatrixXd &invCovariance,
                                             const double nsigmas)
  {
    double sqMahalanobis = innovation.dot(invCovariance * innovation);
    double threshold = nsigmas * nsigmas;

    if (sqMahalanobis >= threshold)
    {
      FB_DEBUG("Innovation mahalanobis distance test failed. Squared Mahalanobis is: " << sqMahalanobis << "\n" <<
               "Threshold is: " << threshold << "\n" <<
               "Innovation is: " << innovation << "\n" <<
               "Innovation covariance is:\n" << invCovariance << "\n");

      return false;
    }

    return true;
  }
}  // namespace RobotLocalization
<|endoftext|>


<commit_before>/**
 * Process control - running other tasks and manipulating their input/output
 */

#include <v8.h>
#include <string>
#include "js_macros.h"
#include "js_common.h"
#include <stdlib.h>
#ifndef _JS_NULL
#define JS_NULL v8::Null()
#endif

namespace {

JS_METHOD(_process) {
	ASSERT_CONSTRUCTOR;
	SAVE_PTR(0, NULL);
	return args.This();
}

JS_METHOD(_system) {
	if (args.Length() != 1) {
		return JS_EXCEPTION("Wrong argument count. Use include(\"process\").system(\"command\")");
	}

	v8::String::Utf8Value cmd(args[0]);
	int result = system(*cmd);
	return JS_INT(result);
}

/**
 * The initial .exec method for stream buffering system commands
 */

JS_METHOD(_exec) {
	if (args.Length() != 1) {
		return JS_EXCEPTION("Wrong argument count. Use include(\"process\").exec(\"command\")");
	}

	std::string data;
	FILE *stream;
	int MAX_BUFFER = 256;
	char buffer[MAX_BUFFER];

	v8::String::Utf8Value cmd(args[0]);
	stream = popen(*cmd, "r");
	while ( fgets(buffer, MAX_BUFFER, stream) != NULL ) {
		data.append(buffer);
	}
	pclose(stream);

	if(data.c_str() != NULL) {
	return JS_STR(data.c_str());
	} else {
	return JS_NULL;
	}
}

}

SHARED_INIT() {
	v8::HandleScope handle_scope;
	v8::Handle<v8::FunctionTemplate> funct = v8::FunctionTemplate::New(_process);
       funct->SetClassName(JS_STR("Process"));
	v8::Handle<v8::ObjectTemplate> ot = funct->InstanceTemplate();
	ot->SetInternalFieldCount(1); /* connection */
	v8::Handle<v8::ObjectTemplate> process = funct->PrototypeTemplate();

	/* this module provides a set of (static) functions */
	process->Set(JS_STR("system"), v8::FunctionTemplate::New(_system)->GetFunction());
	process->Set(JS_STR("exec"), v8::FunctionTemplate::New(_exec)->GetFunction());
	exports->Set(JS_STR("Process"), funct->GetFunction());
}
<commit_msg>whitespace correction<commit_after>/**
 * Process control - running other tasks and manipulating their input/output
 */

#include <v8.h>
#include <string>
#include "js_macros.h"
#include "js_common.h"
#include <stdlib.h>
#ifndef _JS_NULL
#define JS_NULL v8::Null()
#endif

namespace {

JS_METHOD(_process) {
	ASSERT_CONSTRUCTOR;
	SAVE_PTR(0, NULL);
	return args.This();
}

JS_METHOD(_system) {
	if (args.Length() != 1) {
		return JS_EXCEPTION("Wrong argument count. Use include(\"process\").system(\"command\")");
	}

	v8::String::Utf8Value cmd(args[0]);
	int result = system(*cmd);
	return JS_INT(result);
}

/**
 * The initial .exec method for stream buffering system commands
 */

JS_METHOD(_exec) {
	if (args.Length() != 1) {
		return JS_EXCEPTION("Wrong argument count. Use include(\"process\").exec(\"command\")");
	}

	std::string data;
	FILE *stream;
	int MAX_BUFFER = 256;
	char buffer[MAX_BUFFER];

	v8::String::Utf8Value cmd(args[0]);
	stream = popen(*cmd, "r");
	while ( fgets(buffer, MAX_BUFFER, stream) != NULL ) {
		data.append(buffer);
	}
	pclose(stream);

	if(data.c_str() != NULL) {
	return JS_STR(data.c_str());
	} else {
	return JS_NULL;
	}
}

}

SHARED_INIT() {
	v8::HandleScope handle_scope;
	v8::Handle<v8::FunctionTemplate> funct = v8::FunctionTemplate::New(_process);
	funct->SetClassName(JS_STR("Process"));
	v8::Handle<v8::ObjectTemplate> ot = funct->InstanceTemplate();
	ot->SetInternalFieldCount(1);
	v8::Handle<v8::ObjectTemplate> process = funct->PrototypeTemplate();

	/* this module provides a set of (static) functions */
	process->Set(JS_STR("system"), v8::FunctionTemplate::New(_system)->GetFunction());
	process->Set(JS_STR("exec"), v8::FunctionTemplate::New(_exec)->GetFunction());
	exports->Set(JS_STR("Process"), funct->GetFunction());
}
<|endoftext|>


<commit_before>#include "symbols.h"
#include "../codegen/gentype.h"
#include "../../libponyrt/ds/hash.h"
#include "../../libponyrt/mem/pool.h"

#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable:4003)
#  pragma warning(disable:4244)
#  pragma warning(disable:4800)
#  pragma warning(disable:4267)
#  pragma warning(disable:4291)
#endif

#include <llvm/IR/Module.h>
#include <llvm/IR/DebugInfo.h>
#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Support/Path.h>
#include <llvm/IR/Metadata.h>
#include <llvm/Config/llvm-config.h>

#ifdef _MSC_VER
#  pragma warning(pop)
#endif

using namespace llvm;
using namespace llvm::dwarf;

typedef struct debug_sym_t debug_sym_t;
typedef struct debug_frame_t debug_frame_t;

struct debug_sym_t
{
  const char* name;

  DIType type;
  DIType qualified;
  DIType actual;
  DICompositeType prelim;
};

struct debug_frame_t
{
  const char* type_name;

  size_t size;

  std::vector<llvm::Metadata *> members;

  DIDescriptor scope;
  DebugLoc location;

  debug_frame_t* prev;
};

static uint64_t symbol_hash(debug_sym_t* symbol);
static bool symbol_cmp(debug_sym_t* a, debug_sym_t* b);
static void symbol_free(debug_sym_t* symbol);

DECLARE_HASHMAP(symbolmap, debug_sym_t);
DEFINE_HASHMAP(symbolmap, debug_sym_t, symbol_hash, symbol_cmp,
  pool_alloc_size, pool_free_size, symbol_free);

struct symbols_t
{
  symbolmap_t map;

  DIBuilder* builder;
  IRBuilderBase* ir;
  DICompileUnit unit;

  bool release;

  debug_frame_t* frame;
};

static uint64_t symbol_hash(debug_sym_t* symbol)
{
  return hash_ptr(symbol->name);
}

static bool symbol_cmp(debug_sym_t* a, debug_sym_t* b)
{
  return a->name == b->name;
}

static void symbol_free(debug_sym_t* symbol)
{
  POOL_FREE(debug_sym_t, symbol);
}

static debug_sym_t* get_entry(symbols_t* symbols, const char* name)
{
  debug_sym_t key;
  key.name = name;

  debug_sym_t* value = symbolmap_get(&symbols->map, &key);

  if(value == NULL)
  {
    value = POOL_ALLOC(debug_sym_t);
    value->name = key.name;
    value->type = DIType();
    value->qualified = DIType();
    value->prelim = DICompositeType();
    value->actual = DIType();

    symbolmap_put(&symbols->map, value);
  }

  return value;
}

static DIFile get_file(symbols_t* symbols, const char* fullpath)
{
  StringRef name = sys::path::filename(fullpath);
  StringRef path = sys::path::parent_path(fullpath);

  return symbols->builder->createFile(name, path);
}

void symbols_init(symbols_t** symbols, LLVMBuilderRef builder,
  LLVMModuleRef module, bool optimised)
{
  symbols_t* s = *symbols = POOL_ALLOC(symbols_t);
  memset(s, 0, sizeof(symbols_t));

  symbolmap_init(&s->map, 0);

  Module* m = unwrap(module);

#ifndef PLATFORM_IS_WINDOWS
  unsigned version = llvm::DEBUG_METADATA_VERSION;

  m->addModuleFlag(llvm::Module::Warning, "Dwarf Version", version);
  m->addModuleFlag(llvm::Module::Error, "Debug Info Version", version);
#endif

  s->builder = new DIBuilder(*m);
  s->ir = unwrap(builder);
  s->release = optimised;
}

void symbols_push_frame(symbols_t* symbols, gentype_t* g)
{
  debug_frame_t* frame = POOL_ALLOC(debug_frame_t);
  memset(frame, 0, sizeof(debug_frame_t));

  if(g != NULL)
  {
    frame->type_name = g->type_name;
    frame->size = g->field_count;
  }

  frame->prev = symbols->frame;
  symbols->frame = frame;
}

void symbols_pop_frame(symbols_t* symbols)
{
  debug_frame_t* frame = symbols->frame;
  symbols->frame = frame->prev;
  POOL_FREE(debug_frame_t, frame);
}

void symbols_package(symbols_t* symbols, const char* path, const char* name)
{
  symbols->unit = symbols->builder->createCompileUnit(DW_LANG_Pony, name, path,
    DW_TAG_Producer, symbols->release, StringRef(), 0, StringRef(),
    llvm::DIBuilder::FullDebug, true);
}

void symbols_basic(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* d = get_entry(symbols, meta->name);

  uint16_t tag = dwarf::DW_ATE_unsigned;

  switch(meta->flags)
  {
    case DWARF_SIGNED:
      tag = dwarf::DW_ATE_signed;
      break;
    case DWARF_FLOAT:
      tag = dwarf::DW_ATE_float;
      break;
    case DWARF_BOOLEAN:
      tag = dwarf::DW_ATE_boolean;
      break;
    default: {};
  }

  d->type = symbols->builder->createBasicType(meta->name, meta->size,
    meta->align, tag);
  d->actual = d->type;

  // Eventually, basic builtin types may be used as const, e.g. let field or
  // local, method/behaviour parameter.
  d->qualified = symbols->builder->createQualifiedType(DW_TAG_const_type,
    d->type);
}

void symbols_pointer(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* pointer = get_entry(symbols, meta->name);
  debug_sym_t* typearg = get_entry(symbols, meta->typearg);

  pointer->type = symbols->builder->createPointerType(typearg->type,
    meta->size, meta->align);

  pointer->actual = pointer->type;

  pointer->qualified = symbols->builder->createQualifiedType(DW_TAG_const_type,
    pointer->type);
}

void symbols_trait(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* d = get_entry(symbols, meta->name);

  DIFile file = get_file(symbols, meta->file);

  DICompositeType composite = symbols->builder->createClassType(symbols->unit,
    meta->name, file, (int)meta->line, meta->size, meta->align, meta->offset,
    0, DIType(), DIArray());

  d->type = symbols->builder->createPointerType(composite, meta->size,
    meta->align);

  d->actual = d->type;

  d->qualified = symbols->builder->createQualifiedType(DW_TAG_const_type,
    d->type);
}

void symbols_unspecified(symbols_t* symbols, const char* name)
{
  debug_sym_t* d = get_entry(symbols, name);

  DICompositeType type = symbols->builder->createClassType(symbols->unit,
    name, DIFile(), 0, 0, 0, 0, 0, DIType(), DIArray());

  d->type = symbols->builder->createPointerType(type, 0, 0);

  d->actual = d->type;

  d->qualified = symbols->builder->createQualifiedType(DW_TAG_const_type,
    d->type);
}

void symbols_declare(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* d = get_entry(symbols, meta->name);

  DIFile file = get_file(symbols, meta->file);
  uint16_t tag = DW_TAG_class_type;
  uint16_t qualifier = DW_TAG_const_type;

  if(meta->flags & DWARF_TUPLE)
    tag = DW_TAG_structure_type;

#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 6)
  d->prelim = symbols->builder->createReplaceableCompositeType(tag,
    meta->name, symbols->unit, file, (int)meta->line);
#else
  d->prelim = symbols->builder->createReplaceableForwardDecl(tag,
    meta->name, symbols->unit, file, (int)meta->line);
#endif

  if(meta->flags & DWARF_TUPLE)
  {
    // The actual use type is the structure itself.
    d->type = d->prelim;
    d->qualified = symbols->builder->createQualifiedType(qualifier, d->type);
  } else {
    // The use type is a pointer to the structure.
    d->type = symbols->builder->createPointerType(d->prelim,
      meta->size, meta->align);

    // A let field or method parameter is equivalent to a
    // C <type>* const <identifier>.
    d->qualified = symbols->builder->createQualifiedType(qualifier,
      d->type);
  }
}

void symbols_field(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* d = get_entry(symbols, meta->typearg);
  debug_frame_t* frame = symbols->frame;

  unsigned visibility = DW_ACCESS_public;

  if((meta->flags & DWARF_PRIVATE) != 0)
    visibility = DW_ACCESS_private;

  DIType use_type = d->type;

  if(meta->flags & DWARF_CONSTANT)
    use_type = d->qualified;

  DIFile file = get_file(symbols, meta->file);

  DIDerivedType member = symbols->builder->createMemberType(symbols->unit,
    meta->name, file, (int)meta->line, meta->size, meta->align, meta->offset,
    visibility, use_type);

  frame->members.push_back(member);
}

void symbols_method(symbols_t* symbols, dwarf_meta_t* meta, LLVMValueRef ir)
{
  // Emit debug info for the subroutine type.
  std::vector<Metadata*> params;

  // The return type is not const, so don't use the qualified type.
  debug_sym_t* current = get_entry(symbols, meta->params[0]);
  params.push_back(current->type);

  for(size_t i = 1; i < meta->size; i++)
  {
    current = get_entry(symbols, meta->params[i]);
    params.push_back(current->qualified);
  }

  DIFile file = get_file(symbols, meta->file);

  DICompositeType type = symbols->builder->createSubroutineType(file,
    symbols->builder->getOrCreateTypeArray(params));

  Function* f = dyn_cast_or_null<Function>(unwrap(ir));
  debug_sym_t* d = get_entry(symbols, symbols->frame->type_name);

  symbols->frame->scope = symbols->builder->createMethod(d->actual,
    meta->name, meta->mangled, file, (int)meta->line, type, false, true,
    0, 0, DIType(), 0, symbols->release, f);
}

void symbols_composite(symbols_t* symbols, dwarf_meta_t* meta)
{
  // The composite was previously forward declared, and a preliminary
  // debug symbol exists.
  DIFile file = get_file(symbols, meta->file);
  DIArray fields = symbols->builder->getOrCreateArray(symbols->frame->members);

  debug_sym_t* d = get_entry(symbols, meta->name);

  if(meta->flags & DWARF_TUPLE)
  {
    d->actual = symbols->builder->createStructType(symbols->unit,
      meta->name, file, (int)meta->line, meta->size, meta->align, 0, DIType(),
      fields);

    d->type = d->actual;
  } else {
    d->actual = symbols->builder->createClassType(symbols->unit, meta->name,
      file, (int)meta->line, meta->size, meta->align, 0, 0, DIType(), fields);
  }

  d->prelim->replaceAllUsesWith(d->actual);
}

void symbols_lexicalscope(symbols_t* symbols, dwarf_meta_t* meta)
{
  DIDescriptor parent = symbols->frame->prev->scope;
  DIFile file = get_file(symbols, meta->file);

  assert((MDNode*)parent != NULL);

  symbols->frame->scope = symbols->builder->createLexicalBlock(parent,
    file, (unsigned)meta->line, (unsigned)meta->pos);
}

void symbols_local(symbols_t* symbols, dwarf_meta_t* meta, bool is_arg)
{
  unsigned tag = DW_TAG_auto_variable;
  unsigned index = 0;

  debug_sym_t* d = get_entry(symbols, meta->mangled);
  debug_frame_t* frame = symbols->frame;

  DIType type = d->type;
  DIFile file = get_file(symbols, meta->file);

  if(is_arg)
  {
    tag = DW_TAG_arg_variable;
    index = (unsigned)meta->offset;
  }

  if(meta->flags & DWARF_CONSTANT)
    type = d->qualified;

  if(meta->flags & DWARF_ARTIFICIAL)
    type = symbols->builder->createArtificialType(type);

  DIVariable info = symbols->builder->createLocalVariable(tag, frame->scope,
    meta->name, file, (unsigned)meta->line, type, true, 0, index);

  DIExpression complex = symbols->builder->createExpression();
  Value* ref = unwrap(meta->storage);
  Instruction* intrinsic;

  if(meta->inst != NULL)
  {
    Instruction* before = dyn_cast_or_null<Instruction>(unwrap(meta->inst));
    intrinsic = symbols->builder->insertDeclare(ref, info, complex, before);
  } else {
    BasicBlock* end = dyn_cast_or_null<BasicBlock>(unwrap(meta->entry));
    intrinsic = symbols->builder->insertDeclare(ref, info, complex, end);
  }

  intrinsic->setDebugLoc(DebugLoc::get((unsigned)meta->line,
    (unsigned)meta->pos, frame->scope));
}

void symbols_location(symbols_t* symbols, size_t line, size_t pos)
{
  DebugLoc loc =  DebugLoc::get((unsigned)line, (unsigned)pos,
    symbols->frame->scope);

  symbols->frame->location = loc;
  symbols->ir->SetCurrentDebugLocation(loc);
}

void symbols_reset(symbols_t* symbols, bool disable)
{
  if(disable && (symbols->frame != NULL))
  {
    symbols->frame->location = DebugLoc::get(0, 0, NULL);
    symbols->ir->SetCurrentDebugLocation(symbols->frame->location);
  }
  else if(symbols->frame)
  {
    symbols->ir->SetCurrentDebugLocation(symbols->frame->location);
  }
}

void symbols_finalise(symbols_t* symbols)
{
  symbols->builder->finalize();
  delete symbols->builder;

  symbolmap_destroy(&symbols->map);
  POOL_FREE(symbols_t, symbols);
}
<commit_msg>ifdef to allow using llvm 3.6 on linux/mac<commit_after>#include "symbols.h"
#include "../codegen/gentype.h"
#include "../../libponyrt/ds/hash.h"
#include "../../libponyrt/mem/pool.h"

#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable:4003)
#  pragma warning(disable:4244)
#  pragma warning(disable:4800)
#  pragma warning(disable:4267)
#  pragma warning(disable:4291)
#endif

#include <llvm/IR/Module.h>
#include <llvm/IR/DebugInfo.h>
#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Support/Path.h>
#include <llvm/IR/Metadata.h>
#include <llvm/Config/llvm-config.h>

#ifdef _MSC_VER
#  pragma warning(pop)
#endif

using namespace llvm;
using namespace llvm::dwarf;

typedef struct debug_sym_t debug_sym_t;
typedef struct debug_frame_t debug_frame_t;

struct debug_sym_t
{
  const char* name;

  DIType type;
  DIType qualified;
  DIType actual;
  DICompositeType prelim;
};

struct debug_frame_t
{
  const char* type_name;

  size_t size;

  std::vector<llvm::Metadata *> members;

  DIDescriptor scope;
  DebugLoc location;

  debug_frame_t* prev;
};

static uint64_t symbol_hash(debug_sym_t* symbol);
static bool symbol_cmp(debug_sym_t* a, debug_sym_t* b);
static void symbol_free(debug_sym_t* symbol);

DECLARE_HASHMAP(symbolmap, debug_sym_t);
DEFINE_HASHMAP(symbolmap, debug_sym_t, symbol_hash, symbol_cmp,
  pool_alloc_size, pool_free_size, symbol_free);

struct symbols_t
{
  symbolmap_t map;

  DIBuilder* builder;
  IRBuilderBase* ir;
  DICompileUnit unit;

  bool release;

  debug_frame_t* frame;
};

static uint64_t symbol_hash(debug_sym_t* symbol)
{
  return hash_ptr(symbol->name);
}

static bool symbol_cmp(debug_sym_t* a, debug_sym_t* b)
{
  return a->name == b->name;
}

static void symbol_free(debug_sym_t* symbol)
{
  POOL_FREE(debug_sym_t, symbol);
}

static debug_sym_t* get_entry(symbols_t* symbols, const char* name)
{
  debug_sym_t key;
  key.name = name;

  debug_sym_t* value = symbolmap_get(&symbols->map, &key);

  if(value == NULL)
  {
    value = POOL_ALLOC(debug_sym_t);
    value->name = key.name;
    value->type = DIType();
    value->qualified = DIType();
    value->prelim = DICompositeType();
    value->actual = DIType();

    symbolmap_put(&symbols->map, value);
  }

  return value;
}

static DIFile get_file(symbols_t* symbols, const char* fullpath)
{
  StringRef name = sys::path::filename(fullpath);
  StringRef path = sys::path::parent_path(fullpath);

  return symbols->builder->createFile(name, path);
}

void symbols_init(symbols_t** symbols, LLVMBuilderRef builder,
  LLVMModuleRef module, bool optimised)
{
  symbols_t* s = *symbols = POOL_ALLOC(symbols_t);
  memset(s, 0, sizeof(symbols_t));

  symbolmap_init(&s->map, 0);

  Module* m = unwrap(module);

#ifndef PLATFORM_IS_WINDOWS
  unsigned version = llvm::DEBUG_METADATA_VERSION;

  m->addModuleFlag(llvm::Module::Warning, "Dwarf Version", version);
  m->addModuleFlag(llvm::Module::Error, "Debug Info Version", version);
#endif

  s->builder = new DIBuilder(*m);
  s->ir = unwrap(builder);
  s->release = optimised;
}

void symbols_push_frame(symbols_t* symbols, gentype_t* g)
{
  debug_frame_t* frame = POOL_ALLOC(debug_frame_t);
  memset(frame, 0, sizeof(debug_frame_t));

  if(g != NULL)
  {
    frame->type_name = g->type_name;
    frame->size = g->field_count;
  }

  frame->prev = symbols->frame;
  symbols->frame = frame;
}

void symbols_pop_frame(symbols_t* symbols)
{
  debug_frame_t* frame = symbols->frame;
  symbols->frame = frame->prev;
  POOL_FREE(debug_frame_t, frame);
}

void symbols_package(symbols_t* symbols, const char* path, const char* name)
{
  symbols->unit = symbols->builder->createCompileUnit(DW_LANG_Pony, name, path,
    DW_TAG_Producer, symbols->release, StringRef(), 0, StringRef(),
    llvm::DIBuilder::FullDebug, true);
}

void symbols_basic(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* d = get_entry(symbols, meta->name);

  uint16_t tag = dwarf::DW_ATE_unsigned;

  switch(meta->flags)
  {
    case DWARF_SIGNED:
      tag = dwarf::DW_ATE_signed;
      break;
    case DWARF_FLOAT:
      tag = dwarf::DW_ATE_float;
      break;
    case DWARF_BOOLEAN:
      tag = dwarf::DW_ATE_boolean;
      break;
    default: {};
  }

  d->type = symbols->builder->createBasicType(meta->name, meta->size,
    meta->align, tag);
  d->actual = d->type;

  // Eventually, basic builtin types may be used as const, e.g. let field or
  // local, method/behaviour parameter.
  d->qualified = symbols->builder->createQualifiedType(DW_TAG_const_type,
    d->type);
}

void symbols_pointer(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* pointer = get_entry(symbols, meta->name);
  debug_sym_t* typearg = get_entry(symbols, meta->typearg);

  pointer->type = symbols->builder->createPointerType(typearg->type,
    meta->size, meta->align);

  pointer->actual = pointer->type;

  pointer->qualified = symbols->builder->createQualifiedType(DW_TAG_const_type,
    pointer->type);
}

void symbols_trait(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* d = get_entry(symbols, meta->name);

  DIFile file = get_file(symbols, meta->file);

  DICompositeType composite = symbols->builder->createClassType(symbols->unit,
    meta->name, file, (int)meta->line, meta->size, meta->align, meta->offset,
    0, DIType(), DIArray());

  d->type = symbols->builder->createPointerType(composite, meta->size,
    meta->align);

  d->actual = d->type;

  d->qualified = symbols->builder->createQualifiedType(DW_TAG_const_type,
    d->type);
}

void symbols_unspecified(symbols_t* symbols, const char* name)
{
  debug_sym_t* d = get_entry(symbols, name);

  DICompositeType type = symbols->builder->createClassType(symbols->unit,
    name, DIFile(), 0, 0, 0, 0, 0, DIType(), DIArray());

  d->type = symbols->builder->createPointerType(type, 0, 0);

  d->actual = d->type;

  d->qualified = symbols->builder->createQualifiedType(DW_TAG_const_type,
    d->type);
}

void symbols_declare(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* d = get_entry(symbols, meta->name);

  DIFile file = get_file(symbols, meta->file);
  uint16_t tag = DW_TAG_class_type;
  uint16_t qualifier = DW_TAG_const_type;

  if(meta->flags & DWARF_TUPLE)
    tag = DW_TAG_structure_type;

#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 6)
  d->prelim = symbols->builder->createReplaceableCompositeType(tag,
    meta->name, symbols->unit, file, (int)meta->line);
#else
  d->prelim = symbols->builder->createReplaceableForwardDecl(tag,
    meta->name, symbols->unit, file, (int)meta->line);
#endif

  if(meta->flags & DWARF_TUPLE)
  {
    // The actual use type is the structure itself.
    d->type = d->prelim;
    d->qualified = symbols->builder->createQualifiedType(qualifier, d->type);
  } else {
    // The use type is a pointer to the structure.
    d->type = symbols->builder->createPointerType(d->prelim,
      meta->size, meta->align);

    // A let field or method parameter is equivalent to a
    // C <type>* const <identifier>.
    d->qualified = symbols->builder->createQualifiedType(qualifier,
      d->type);
  }
}

void symbols_field(symbols_t* symbols, dwarf_meta_t* meta)
{
  debug_sym_t* d = get_entry(symbols, meta->typearg);
  debug_frame_t* frame = symbols->frame;

  unsigned visibility = DW_ACCESS_public;

  if((meta->flags & DWARF_PRIVATE) != 0)
    visibility = DW_ACCESS_private;

  DIType use_type = d->type;

  if(meta->flags & DWARF_CONSTANT)
    use_type = d->qualified;

  DIFile file = get_file(symbols, meta->file);

  DIDerivedType member = symbols->builder->createMemberType(symbols->unit,
    meta->name, file, (int)meta->line, meta->size, meta->align, meta->offset,
    visibility, use_type);

  frame->members.push_back(member);
}

void symbols_method(symbols_t* symbols, dwarf_meta_t* meta, LLVMValueRef ir)
{
  // Emit debug info for the subroutine type.
  std::vector<Metadata*> params;

  // The return type is not const, so don't use the qualified type.
  debug_sym_t* current = get_entry(symbols, meta->params[0]);
  params.push_back(current->type);

  for(size_t i = 1; i < meta->size; i++)
  {
    current = get_entry(symbols, meta->params[i]);
    params.push_back(current->qualified);
  }

  DIFile file = get_file(symbols, meta->file);

  DICompositeType type = symbols->builder->createSubroutineType(file,
    symbols->builder->getOrCreateTypeArray(params));

  Function* f = dyn_cast_or_null<Function>(unwrap(ir));
  debug_sym_t* d = get_entry(symbols, symbols->frame->type_name);

  symbols->frame->scope = symbols->builder->createMethod(d->actual,
    meta->name, meta->mangled, file, (int)meta->line, type, false, true,
    0, 0, DIType(), 0, symbols->release, f);
}

void symbols_composite(symbols_t* symbols, dwarf_meta_t* meta)
{
  // The composite was previously forward declared, and a preliminary
  // debug symbol exists.
  DIFile file = get_file(symbols, meta->file);
  DIArray fields = symbols->builder->getOrCreateArray(symbols->frame->members);

  debug_sym_t* d = get_entry(symbols, meta->name);

  if(meta->flags & DWARF_TUPLE)
  {
    d->actual = symbols->builder->createStructType(symbols->unit,
      meta->name, file, (int)meta->line, meta->size, meta->align, 0, DIType(),
      fields);

    d->type = d->actual;
  } else {
    d->actual = symbols->builder->createClassType(symbols->unit, meta->name,
      file, (int)meta->line, meta->size, meta->align, 0, 0, DIType(), fields);
  }

  #if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 6)
    d->prelim->replaceAllUsesWith(d->actual);
  #else
    d->prelim.replaceAllUsesWith(d->actual);
  #endif
}

void symbols_lexicalscope(symbols_t* symbols, dwarf_meta_t* meta)
{
  DIDescriptor parent = symbols->frame->prev->scope;
  DIFile file = get_file(symbols, meta->file);

  assert((MDNode*)parent != NULL);

  symbols->frame->scope = symbols->builder->createLexicalBlock(parent,
    file, (unsigned)meta->line, (unsigned)meta->pos);
}

void symbols_local(symbols_t* symbols, dwarf_meta_t* meta, bool is_arg)
{
  unsigned tag = DW_TAG_auto_variable;
  unsigned index = 0;

  debug_sym_t* d = get_entry(symbols, meta->mangled);
  debug_frame_t* frame = symbols->frame;

  DIType type = d->type;
  DIFile file = get_file(symbols, meta->file);

  if(is_arg)
  {
    tag = DW_TAG_arg_variable;
    index = (unsigned)meta->offset;
  }

  if(meta->flags & DWARF_CONSTANT)
    type = d->qualified;

  if(meta->flags & DWARF_ARTIFICIAL)
    type = symbols->builder->createArtificialType(type);

  DIVariable info = symbols->builder->createLocalVariable(tag, frame->scope,
    meta->name, file, (unsigned)meta->line, type, true, 0, index);

  DIExpression complex = symbols->builder->createExpression();
  Value* ref = unwrap(meta->storage);
  Instruction* intrinsic;

  if(meta->inst != NULL)
  {
    Instruction* before = dyn_cast_or_null<Instruction>(unwrap(meta->inst));
    intrinsic = symbols->builder->insertDeclare(ref, info, complex, before);
  } else {
    BasicBlock* end = dyn_cast_or_null<BasicBlock>(unwrap(meta->entry));
    intrinsic = symbols->builder->insertDeclare(ref, info, complex, end);
  }

  intrinsic->setDebugLoc(DebugLoc::get((unsigned)meta->line,
    (unsigned)meta->pos, frame->scope));
}

void symbols_location(symbols_t* symbols, size_t line, size_t pos)
{
  DebugLoc loc =  DebugLoc::get((unsigned)line, (unsigned)pos,
    symbols->frame->scope);

  symbols->frame->location = loc;
  symbols->ir->SetCurrentDebugLocation(loc);
}

void symbols_reset(symbols_t* symbols, bool disable)
{
  if(disable && (symbols->frame != NULL))
  {
    symbols->frame->location = DebugLoc::get(0, 0, NULL);
    symbols->ir->SetCurrentDebugLocation(symbols->frame->location);
  }
  else if(symbols->frame)
  {
    symbols->ir->SetCurrentDebugLocation(symbols->frame->location);
  }
}

void symbols_finalise(symbols_t* symbols)
{
  symbols->builder->finalize();
  delete symbols->builder;

  symbolmap_destroy(&symbols->map);
  POOL_FREE(symbols_t, symbols);
}
<|endoftext|>


<commit_before>/*
  Copyright (C) 2008-2014 The Communi Project

  You may use this file under the terms of BSD license as follows:

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "textbrowser.h"
#include "textdocument.h"
#include <QAbstractTextDocumentLayout>
#include <QDesktopServices>
#include <QStylePainter>
#include <QStyleOption>
#include <QApplication>
#include <QTextBlock>
#include <IrcCommand>
#include <QScrollBar>
#include <IrcBuffer>
#include <QKeyEvent>
#include <QPainter>
#include <QToolTip>
#include <QAction>
#include <QMenu>

class TextShadow : public QFrame { Q_OBJECT };

TextBrowser::TextBrowser(QWidget* parent) : QTextBrowser(parent)
{
    d.bud = 0;
    d.events = true;
    d.shadow = new TextShadow;
    d.shadow->setParent(this);

    setOpenLinks(false);
    setTabChangesFocus(true);
    setContextMenuPolicy(Qt::CustomContextMenu);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    connect(this, SIGNAL(anchorClicked(QUrl)), this, SLOT(onAnchorClicked(QUrl)));
}

TextBrowser::~TextBrowser()
{
    TextDocument* doc = document();
    if (doc) {
        if (doc->isClone())
            delete doc;
        else
            doc->setVisible(false);
    }
}

IrcBuffer* TextBrowser::buffer() const
{
    TextDocument* doc = document();
    if (doc)
        return doc->buffer();
    return 0;
}

TextDocument* TextBrowser::document() const
{
    return qobject_cast<TextDocument*>(QTextBrowser::document());
}

void TextBrowser::setDocument(TextDocument* document)
{
    TextDocument* doc = qobject_cast<TextDocument*>(QTextBrowser::document());
    if (doc != document) {
        if (doc) {
            doc->setVisible(false);
            disconnect(doc->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), this, SLOT(keepAtBottom()));
            disconnect(doc, SIGNAL(lineRemoved(int)), this, SLOT(keepPosition(int)));
        }
        if (document) {
            document->setVisible(true);
            document->setDefaultFont(font());
            connect(document->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), this, SLOT(keepAtBottom()));
            connect(document, SIGNAL(lineRemoved(int)), this, SLOT(keepPosition(int)));
        }
        connect(this, SIGNAL(textChanged()), this, SLOT(moveCursorToBottom()));
        QTextBrowser::setDocument(document);
        disconnect(this, SIGNAL(textChanged()), this, SLOT(moveCursorToBottom()));
        scrollToBottom();
        emit documentChanged(document);
    }
}

QWidget* TextBrowser::buddy() const
{
    return d.bud;
}

void TextBrowser::setBuddy(QWidget* buddy)
{
    d.bud = buddy;
}

void TextBrowser::mousePressEvent(QMouseEvent* event)
{
    const QUrl url(anchorAt(event->pos()));
    if (url.scheme() == "expand") {
        QString text;
        if (TextDocument* doc = document()) {
            const QPoint offset(horizontalScrollBar()->value(), verticalScrollBar()->value());
            text = doc->tooltip(event->pos() + offset);
        }
        if (!text.isEmpty())
            QToolTip::showText(event->globalPos(), text, viewport());
    } else if (url.scheme() == "nick") {
        QMenu* menu = createContextMenu(event->pos());
        menu->exec(event->globalPos());
        menu->deleteLater();
    }
    QTextBrowser::mousePressEvent(event);
}

void TextBrowser::mouseMoveEvent(QMouseEvent* event)
{
    QToolTip::hideText();
    QTextBrowser::mouseMoveEvent(event);
}

void TextBrowser::keyPressEvent(QKeyEvent* event)
{
    // for example:
    // - Ctrl+C goes to the browser
    // - Ctrl+V goes to the buddy
    // - Shift+7 ("/") goes to the buddy
    if (d.bud) {
        switch (event->key()) {
            case Qt::Key_Shift:
            case Qt::Key_Control:
            case Qt::Key_Meta:
            case Qt::Key_Alt:
            case Qt::Key_AltGr:
                break;
            default:
                if (!event->matches(QKeySequence::Copy) && !event->matches(QKeySequence::SelectAll)) {
                    QCoreApplication::sendEvent(d.bud, event);
                    QWidget* focus = qApp->focusWidget();
                    if (!focus || !focus->inherits("QLineEdit") || (focus->inherits("TextInput") && !isAncestorOf(focus)))
                        d.bud->setFocus();
                    return;
                }
                break;
        }
    }
    QTextBrowser::keyPressEvent(event);
}

void TextBrowser::resizeEvent(QResizeEvent* event)
{
    QTextBrowser::resizeEvent(event);

    d.shadow->resize(width(), d.shadow->height());

    // http://www.qtsoftware.com/developer/task-tracker/index_html?method=entry&id=240940
    QMetaObject::invokeMethod(this, "scrollToBottom", Qt::QueuedConnection);
}

bool TextBrowser::isAtTop() const
{
    return verticalScrollBar()->value() <= verticalScrollBar()->minimum();
}

bool TextBrowser::isAtBottom() const
{
    return verticalScrollBar()->value() >= verticalScrollBar()->maximum();
}

bool TextBrowser::isZoomed() const
{
    QFont f = font();
    if (f.pointSize() != -1)
        return f.pointSize() != QFont().pointSize();
    return f.pixelSize() != QFont().pixelSize();
}

QMenu* TextBrowser::createContextMenu(const QPoint& pos)
{
    // QTextEdit::createStandardContextMenu() expects document coordinates
    QPoint docPos = pos;
    QScrollBar* hbar = horizontalScrollBar();
    docPos.rx() += isRightToLeft() ? hbar->maximum() - hbar->value() : hbar->value();
    docPos.ry() += verticalScrollBar()->value();

    QMenu* menu = createStandardContextMenu(docPos);

    const QString anchor = anchorAt(pos);
    if (anchor.startsWith("nick:")) {
        // disable "Copy Link Location" for nicks
        QAction* action = menu->actions().value(1);
        if (action)
            action->setDisabled(true);

        // inject query & whois
        QAction* separator = menu->insertSeparator(menu->actions().value(0));

        QAction* queryAction = new QAction(tr("Query"), menu);
        menu->insertAction(separator, queryAction);
        connect(queryAction, SIGNAL(triggered()), this, SLOT(onQueryTriggered()));

        QAction* whoisAction = new QAction(tr("Whois"), menu);
        menu->insertAction(queryAction, whoisAction);
        connect(whoisAction, SIGNAL(triggered()), this, SLOT(onWhoisTriggered()));

        QString nick = QUrl(anchor).toString(QUrl::RemoveScheme | QUrl::RemoveFragment);

        QAction* nickAction = new QAction(nick, menu);
        menu->insertAction(whoisAction, nickAction);
        nickAction->setEnabled(false);
        menu->insertSeparator(whoisAction);

        nickAction->setText(nick);
        queryAction->setData(nick);
        whoisAction->setData(nick);
    }
    return menu;
}

void TextBrowser::clear()
{
    QTextBrowser::clear();
    TextDocument* doc = document();
    if (doc)
        doc->reset();
}

void TextBrowser::resetZoom()
{
    QFont f = font();
    f.setPointSize(QFont().pointSize());
    setFont(f);
}

void TextBrowser::scrollToTop()
{
    verticalScrollBar()->triggerAction(QScrollBar::SliderToMinimum);
}

void TextBrowser::scrollToBottom()
{
    verticalScrollBar()->triggerAction(QScrollBar::SliderToMaximum);
}

void TextBrowser::scrollToNextPage()
{
    verticalScrollBar()->triggerAction(QScrollBar::SliderPageStepAdd);
}

void TextBrowser::scrollToPreviousPage()
{
    verticalScrollBar()->triggerAction(QScrollBar::SliderPageStepSub);
}

void TextBrowser::paintEvent(QPaintEvent* event)
{
    // TODO: make sure the shadow is always on top (of transient scrollbars)
    d.shadow->raise();

    const int hoffset = horizontalScrollBar()->value();
    const int voffset = verticalScrollBar()->value();
    const QRect bounds = rect().translated(hoffset, voffset);

    TextDocument* doc = document();
    if (doc) {
        QPainter painter(viewport());
        painter.translate(-hoffset, -voffset);
        doc->drawBackground(&painter, bounds);
    }

    QTextBrowser::paintEvent(event);

    if (doc) {
        QPainter painter(viewport());
        painter.translate(-hoffset, -voffset);
        doc->drawForeground(&painter, bounds);
    }
}

void TextBrowser::wheelEvent(QWheelEvent* event)
{
#ifdef Q_OS_MAC
    // disable cmd+wheel zooming on mac
    QAbstractScrollArea::wheelEvent(event);
#else
    QTextBrowser::wheelEvent(event);
#endif // Q_OS_MAC
}

void TextBrowser::keepAtBottom()
{
    if (isAtBottom())
        QMetaObject::invokeMethod(this, "scrollToBottom", Qt::QueuedConnection);
}

void TextBrowser::keepPosition(int delta)
{
    if (!isAtBottom())
        verticalScrollBar()->setValue(verticalScrollBar()->value() - delta);
}

void TextBrowser::moveCursorToBottom()
{
    QTextCursor cursor = textCursor();
    cursor.movePosition(QTextCursor::End);
    setTextCursor(cursor);
}

void TextBrowser::moveShadow(int offset)
{
    d.shadow->move(0, offset);
}

void TextBrowser::onAnchorClicked(const QUrl& url)
{
    if (url.scheme() != "expand" && url.scheme() != "nick")
        QDesktopServices::openUrl(url);
    clearFocus();
    d.bud->setFocus();
}

void TextBrowser::onWhoisTriggered()
{
    QAction* action = qobject_cast<QAction*>(sender());
    if (action) {
        IrcCommand* command = IrcCommand::createWhois(action->data().toString());
        buffer()->sendCommand(command);
    }
}

void TextBrowser::onQueryTriggered()
{
    QAction* action = qobject_cast<QAction*>(sender());
    if (action)
        emit queried(action->data().toString());
}

#include "textbrowser.moc"
<commit_msg>Make page up/down shortcuts scroll 2/3 of the page<commit_after>/*
  Copyright (C) 2008-2014 The Communi Project

  You may use this file under the terms of BSD license as follows:

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "textbrowser.h"
#include "textdocument.h"
#include <QAbstractTextDocumentLayout>
#include <QDesktopServices>
#include <QStylePainter>
#include <QStyleOption>
#include <QApplication>
#include <QTextBlock>
#include <IrcCommand>
#include <QScrollBar>
#include <IrcBuffer>
#include <QKeyEvent>
#include <QPainter>
#include <QToolTip>
#include <QAction>
#include <QMenu>

class TextShadow : public QFrame { Q_OBJECT };

TextBrowser::TextBrowser(QWidget* parent) : QTextBrowser(parent)
{
    d.bud = 0;
    d.events = true;
    d.shadow = new TextShadow;
    d.shadow->setParent(this);

    setOpenLinks(false);
    setTabChangesFocus(true);
    setContextMenuPolicy(Qt::CustomContextMenu);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    connect(this, SIGNAL(anchorClicked(QUrl)), this, SLOT(onAnchorClicked(QUrl)));
}

TextBrowser::~TextBrowser()
{
    TextDocument* doc = document();
    if (doc) {
        if (doc->isClone())
            delete doc;
        else
            doc->setVisible(false);
    }
}

IrcBuffer* TextBrowser::buffer() const
{
    TextDocument* doc = document();
    if (doc)
        return doc->buffer();
    return 0;
}

TextDocument* TextBrowser::document() const
{
    return qobject_cast<TextDocument*>(QTextBrowser::document());
}

void TextBrowser::setDocument(TextDocument* document)
{
    TextDocument* doc = qobject_cast<TextDocument*>(QTextBrowser::document());
    if (doc != document) {
        if (doc) {
            doc->setVisible(false);
            disconnect(doc->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), this, SLOT(keepAtBottom()));
            disconnect(doc, SIGNAL(lineRemoved(int)), this, SLOT(keepPosition(int)));
        }
        if (document) {
            document->setVisible(true);
            document->setDefaultFont(font());
            connect(document->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), this, SLOT(keepAtBottom()));
            connect(document, SIGNAL(lineRemoved(int)), this, SLOT(keepPosition(int)));
        }
        connect(this, SIGNAL(textChanged()), this, SLOT(moveCursorToBottom()));
        QTextBrowser::setDocument(document);
        disconnect(this, SIGNAL(textChanged()), this, SLOT(moveCursorToBottom()));
        scrollToBottom();
        emit documentChanged(document);
    }
}

QWidget* TextBrowser::buddy() const
{
    return d.bud;
}

void TextBrowser::setBuddy(QWidget* buddy)
{
    d.bud = buddy;
}

void TextBrowser::mousePressEvent(QMouseEvent* event)
{
    const QUrl url(anchorAt(event->pos()));
    if (url.scheme() == "expand") {
        QString text;
        if (TextDocument* doc = document()) {
            const QPoint offset(horizontalScrollBar()->value(), verticalScrollBar()->value());
            text = doc->tooltip(event->pos() + offset);
        }
        if (!text.isEmpty())
            QToolTip::showText(event->globalPos(), text, viewport());
    } else if (url.scheme() == "nick") {
        QMenu* menu = createContextMenu(event->pos());
        menu->exec(event->globalPos());
        menu->deleteLater();
    }
    QTextBrowser::mousePressEvent(event);
}

void TextBrowser::mouseMoveEvent(QMouseEvent* event)
{
    QToolTip::hideText();
    QTextBrowser::mouseMoveEvent(event);
}

void TextBrowser::keyPressEvent(QKeyEvent* event)
{
    // for example:
    // - Ctrl+C goes to the browser
    // - Ctrl+V goes to the buddy
    // - Shift+7 ("/") goes to the buddy
    if (d.bud) {
        switch (event->key()) {
            case Qt::Key_Shift:
            case Qt::Key_Control:
            case Qt::Key_Meta:
            case Qt::Key_Alt:
            case Qt::Key_AltGr:
                break;
            default:
                if (!event->matches(QKeySequence::Copy) && !event->matches(QKeySequence::SelectAll)) {
                    QCoreApplication::sendEvent(d.bud, event);
                    QWidget* focus = qApp->focusWidget();
                    if (!focus || !focus->inherits("QLineEdit") || (focus->inherits("TextInput") && !isAncestorOf(focus)))
                        d.bud->setFocus();
                    return;
                }
                break;
        }
    }
    QTextBrowser::keyPressEvent(event);
}

void TextBrowser::resizeEvent(QResizeEvent* event)
{
    QTextBrowser::resizeEvent(event);

    d.shadow->resize(width(), d.shadow->height());

    // http://www.qtsoftware.com/developer/task-tracker/index_html?method=entry&id=240940
    QMetaObject::invokeMethod(this, "scrollToBottom", Qt::QueuedConnection);
}

bool TextBrowser::isAtTop() const
{
    return verticalScrollBar()->value() <= verticalScrollBar()->minimum();
}

bool TextBrowser::isAtBottom() const
{
    return verticalScrollBar()->value() >= verticalScrollBar()->maximum();
}

bool TextBrowser::isZoomed() const
{
    QFont f = font();
    if (f.pointSize() != -1)
        return f.pointSize() != QFont().pointSize();
    return f.pixelSize() != QFont().pixelSize();
}

QMenu* TextBrowser::createContextMenu(const QPoint& pos)
{
    // QTextEdit::createStandardContextMenu() expects document coordinates
    QPoint docPos = pos;
    QScrollBar* hbar = horizontalScrollBar();
    docPos.rx() += isRightToLeft() ? hbar->maximum() - hbar->value() : hbar->value();
    docPos.ry() += verticalScrollBar()->value();

    QMenu* menu = createStandardContextMenu(docPos);

    const QString anchor = anchorAt(pos);
    if (anchor.startsWith("nick:")) {
        // disable "Copy Link Location" for nicks
        QAction* action = menu->actions().value(1);
        if (action)
            action->setDisabled(true);

        // inject query & whois
        QAction* separator = menu->insertSeparator(menu->actions().value(0));

        QAction* queryAction = new QAction(tr("Query"), menu);
        menu->insertAction(separator, queryAction);
        connect(queryAction, SIGNAL(triggered()), this, SLOT(onQueryTriggered()));

        QAction* whoisAction = new QAction(tr("Whois"), menu);
        menu->insertAction(queryAction, whoisAction);
        connect(whoisAction, SIGNAL(triggered()), this, SLOT(onWhoisTriggered()));

        QString nick = QUrl(anchor).toString(QUrl::RemoveScheme | QUrl::RemoveFragment);

        QAction* nickAction = new QAction(nick, menu);
        menu->insertAction(whoisAction, nickAction);
        nickAction->setEnabled(false);
        menu->insertSeparator(whoisAction);

        nickAction->setText(nick);
        queryAction->setData(nick);
        whoisAction->setData(nick);
    }
    return menu;
}

void TextBrowser::clear()
{
    QTextBrowser::clear();
    TextDocument* doc = document();
    if (doc)
        doc->reset();
}

void TextBrowser::resetZoom()
{
    QFont f = font();
    f.setPointSize(QFont().pointSize());
    setFont(f);
}

void TextBrowser::scrollToTop()
{
    verticalScrollBar()->triggerAction(QScrollBar::SliderToMinimum);
}

void TextBrowser::scrollToBottom()
{
    verticalScrollBar()->triggerAction(QScrollBar::SliderToMaximum);
}

static void scrollPage(QScrollBar* bar, qreal f)
{
    int add = f * bar->pageStep();
    int pos = bar->value() + add;
    if (add > 0 && pos < bar->value())
        pos = bar->maximum();
    else if (add < 0 && pos > bar->value())
        pos = bar->minimum();
    bar->setSliderPosition(pos);
}

void TextBrowser::scrollToNextPage()
{
    scrollPage(verticalScrollBar(), 0.667);
}

void TextBrowser::scrollToPreviousPage()
{
    scrollPage(verticalScrollBar(), -0.667);
}

void TextBrowser::paintEvent(QPaintEvent* event)
{
    // TODO: make sure the shadow is always on top (of transient scrollbars)
    d.shadow->raise();

    const int hoffset = horizontalScrollBar()->value();
    const int voffset = verticalScrollBar()->value();
    const QRect bounds = rect().translated(hoffset, voffset);

    TextDocument* doc = document();
    if (doc) {
        QPainter painter(viewport());
        painter.translate(-hoffset, -voffset);
        doc->drawBackground(&painter, bounds);
    }

    QTextBrowser::paintEvent(event);

    if (doc) {
        QPainter painter(viewport());
        painter.translate(-hoffset, -voffset);
        doc->drawForeground(&painter, bounds);
    }
}

void TextBrowser::wheelEvent(QWheelEvent* event)
{
#ifdef Q_OS_MAC
    // disable cmd+wheel zooming on mac
    QAbstractScrollArea::wheelEvent(event);
#else
    QTextBrowser::wheelEvent(event);
#endif // Q_OS_MAC
}

void TextBrowser::keepAtBottom()
{
    if (isAtBottom())
        QMetaObject::invokeMethod(this, "scrollToBottom", Qt::QueuedConnection);
}

void TextBrowser::keepPosition(int delta)
{
    if (!isAtBottom())
        verticalScrollBar()->setValue(verticalScrollBar()->value() - delta);
}

void TextBrowser::moveCursorToBottom()
{
    QTextCursor cursor = textCursor();
    cursor.movePosition(QTextCursor::End);
    setTextCursor(cursor);
}

void TextBrowser::moveShadow(int offset)
{
    d.shadow->move(0, offset);
}

void TextBrowser::onAnchorClicked(const QUrl& url)
{
    if (url.scheme() != "expand" && url.scheme() != "nick")
        QDesktopServices::openUrl(url);
    clearFocus();
    d.bud->setFocus();
}

void TextBrowser::onWhoisTriggered()
{
    QAction* action = qobject_cast<QAction*>(sender());
    if (action) {
        IrcCommand* command = IrcCommand::createWhois(action->data().toString());
        buffer()->sendCommand(command);
    }
}

void TextBrowser::onQueryTriggered()
{
    QAction* action = qobject_cast<QAction*>(sender());
    if (action)
        emit queried(action->data().toString());
}

#include "textbrowser.moc"
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used 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.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "qmljscheck.h"
#include "qmljsbind.h"
#include "qmljsinterpreter.h"
#include "qmljsevaluate.h"
#include "parser/qmljsast_p.h"

#include <QtCore/QDebug>

using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;

Check::Check(Document::Ptr doc, const Snapshot &snapshot)
    : _doc(doc)
    , _snapshot(snapshot)
    , _context(&_engine)
    , _link(&_context, doc, snapshot)
    , _extraScope(0)
    , _allowAnyProperty(false)
{
}

Check::~Check()
{
}

QList<DiagnosticMessage> Check::operator()()
{
    _messages.clear();
    Node::accept(_doc->ast(), this);
    return _messages;
}

bool Check::visit(UiProgram *ast)
{
    // build the initial scope chain
    if (ast->members && ast->members->member)
        _link.scopeChainAt(_doc, ast->members->member);

    return true;
}

bool Check::visit(UiObjectDefinition *ast)
{
    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

bool Check::visit(UiObjectBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
                           UiObjectInitializer *initializer)
{
    // If the 'typeId' starts with a lower-case letter, it doesn't define
    // a new object instance. For instance: anchors { ... }
    if (typeId->name->asString().at(0).isLower() && ! typeId->next) {
        checkScopeObjectMember(typeId);
        // ### don't give up!
        return;
    }

    if (! _context.lookupType(_doc.data(), typeId)) {
        warning(typeId->identifierToken, QLatin1String("unknown type"));
        // ### don't give up!
        return;
    }

    const ObjectValue *oldScopeObject = _context.qmlScopeObject();
    const ObjectValue *oldExtraScope = _extraScope;
    const bool oldAllowAnyProperty = _allowAnyProperty;
    const ObjectValue *scopeObject = _doc->bind()->findQmlObject(ast);
    _context.setQmlScopeObject(scopeObject);

#ifndef NO_DECLARATIVE_BACKEND
    // check if the object has a Qt.ListElement ancestor
    const ObjectValue *prototype = scopeObject->prototype(&_context);
    while (prototype) {
        if (const QmlObjectValue *qmlMetaObject = dynamic_cast<const QmlObjectValue *>(prototype)) {
            // ### Also check for Qt package. Involves changes in QmlObjectValue.
            if (qmlMetaObject->qmlTypeName() == QLatin1String("ListElement")) {
                _allowAnyProperty = true;
                break;
            }
        }
        prototype = prototype->prototype(&_context);
    }

    // check if the object has a Qt.PropertyChanges ancestor
    prototype = scopeObject->prototype(&_context);
    while (prototype) {
        if (const QmlObjectValue *qmlMetaObject = dynamic_cast<const QmlObjectValue *>(prototype)) {
            // ### Also check for Qt package. Involves changes in QmlObjectValue.
            if (qmlMetaObject->qmlTypeName() == QLatin1String("PropertyChanges"))
                break;
        }
        prototype = prototype->prototype(&_context);
    }
    // find the target script binding
    if (prototype && initializer) {
        for (UiObjectMemberList *m = initializer->members; m; m = m->next) {
            if (UiScriptBinding *scriptBinding = cast<UiScriptBinding *>(m->member)) {
                if (scriptBinding->qualifiedId
                        && scriptBinding->qualifiedId->name->asString() == QLatin1String("target")
                        && ! scriptBinding->qualifiedId->next) {
                    if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(scriptBinding->statement)) {
                        Evaluate evaluator(&_context);
                        const Value *targetValue = evaluator(expStmt->expression);

                        if (const ObjectValue *target = value_cast<const ObjectValue *>(targetValue)) {
                            _extraScope = target;
                        } else {
                            _allowAnyProperty = true;
                        }
                    }
                }
            }
        }
    }
#endif

    Node::accept(initializer, this);

    _context.setQmlScopeObject(oldScopeObject);
    _extraScope = oldExtraScope;
    _allowAnyProperty = oldAllowAnyProperty;
}

bool Check::visit(UiScriptBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    return true;
}

bool Check::visit(UiArrayBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    return true;
}

void Check::checkScopeObjectMember(const AST::UiQualifiedId *id)
{
    if (_allowAnyProperty)
        return;

    const ObjectValue *scopeObject = _context.qmlScopeObject();

    if (! id)
        return; // ### error?

    const QString propertyName = id->name->asString();

    if (propertyName == QLatin1String("id") && ! id->next)
        return;

    // attached properties
    if (! propertyName.isEmpty() && propertyName[0].isUpper())
        scopeObject = _context.typeEnvironment(_doc.data());

    if (! scopeObject)
        return;

    const Value *value = scopeObject->lookupMember(propertyName, &_context);
    if (_extraScope && !value)
        value = _extraScope->lookupMember(propertyName, &_context);
    if (!value) {
        error(id->identifierToken,
              QString("'%1' is not a valid property name").arg(propertyName));
    }

    // ### check for rest of qualifiedId
}

void Check::error(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message));
}

void Check::warning(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message));
}
<commit_msg>Error when binding to nonexistant member of property in Qml.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used 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.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "qmljscheck.h"
#include "qmljsbind.h"
#include "qmljsinterpreter.h"
#include "qmljsevaluate.h"
#include "parser/qmljsast_p.h"

#include <QtCore/QDebug>

using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;

Check::Check(Document::Ptr doc, const Snapshot &snapshot)
    : _doc(doc)
    , _snapshot(snapshot)
    , _context(&_engine)
    , _link(&_context, doc, snapshot)
    , _extraScope(0)
    , _allowAnyProperty(false)
{
}

Check::~Check()
{
}

QList<DiagnosticMessage> Check::operator()()
{
    _messages.clear();
    Node::accept(_doc->ast(), this);
    return _messages;
}

bool Check::visit(UiProgram *ast)
{
    // build the initial scope chain
    if (ast->members && ast->members->member)
        _link.scopeChainAt(_doc, ast->members->member);

    return true;
}

bool Check::visit(UiObjectDefinition *ast)
{
    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

bool Check::visit(UiObjectBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
                           UiObjectInitializer *initializer)
{
    // If the 'typeId' starts with a lower-case letter, it doesn't define
    // a new object instance. For instance: anchors { ... }
    if (typeId->name->asString().at(0).isLower() && ! typeId->next) {
        checkScopeObjectMember(typeId);
        // ### don't give up!
        return;
    }

    if (! _context.lookupType(_doc.data(), typeId)) {
        warning(typeId->identifierToken, QLatin1String("unknown type"));
        // ### don't give up!
        return;
    }

    const ObjectValue *oldScopeObject = _context.qmlScopeObject();
    const ObjectValue *oldExtraScope = _extraScope;
    const bool oldAllowAnyProperty = _allowAnyProperty;
    const ObjectValue *scopeObject = _doc->bind()->findQmlObject(ast);
    _context.setQmlScopeObject(scopeObject);

#ifndef NO_DECLARATIVE_BACKEND
    // check if the object has a Qt.ListElement ancestor
    const ObjectValue *prototype = scopeObject->prototype(&_context);
    while (prototype) {
        if (const QmlObjectValue *qmlMetaObject = dynamic_cast<const QmlObjectValue *>(prototype)) {
            // ### Also check for Qt package. Involves changes in QmlObjectValue.
            if (qmlMetaObject->qmlTypeName() == QLatin1String("ListElement")) {
                _allowAnyProperty = true;
                break;
            }
        }
        prototype = prototype->prototype(&_context);
    }

    // check if the object has a Qt.PropertyChanges ancestor
    prototype = scopeObject->prototype(&_context);
    while (prototype) {
        if (const QmlObjectValue *qmlMetaObject = dynamic_cast<const QmlObjectValue *>(prototype)) {
            // ### Also check for Qt package. Involves changes in QmlObjectValue.
            if (qmlMetaObject->qmlTypeName() == QLatin1String("PropertyChanges"))
                break;
        }
        prototype = prototype->prototype(&_context);
    }
    // find the target script binding
    if (prototype && initializer) {
        for (UiObjectMemberList *m = initializer->members; m; m = m->next) {
            if (UiScriptBinding *scriptBinding = cast<UiScriptBinding *>(m->member)) {
                if (scriptBinding->qualifiedId
                        && scriptBinding->qualifiedId->name->asString() == QLatin1String("target")
                        && ! scriptBinding->qualifiedId->next) {
                    if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(scriptBinding->statement)) {
                        Evaluate evaluator(&_context);
                        const Value *targetValue = evaluator(expStmt->expression);

                        if (const ObjectValue *target = value_cast<const ObjectValue *>(targetValue)) {
                            _extraScope = target;
                        } else {
                            _allowAnyProperty = true;
                        }
                    }
                }
            }
        }
    }
#endif

    Node::accept(initializer, this);

    _context.setQmlScopeObject(oldScopeObject);
    _extraScope = oldExtraScope;
    _allowAnyProperty = oldAllowAnyProperty;
}

bool Check::visit(UiScriptBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    return true;
}

bool Check::visit(UiArrayBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    return true;
}

void Check::checkScopeObjectMember(const UiQualifiedId *id)
{
    if (_allowAnyProperty)
        return;

    const ObjectValue *scopeObject = _context.qmlScopeObject();

    if (! id)
        return; // ### error?

    QString propertyName = id->name->asString();

    if (propertyName == QLatin1String("id") && ! id->next)
        return;

    // attached properties
    if (! propertyName.isEmpty() && propertyName[0].isUpper())
        scopeObject = _context.typeEnvironment(_doc.data());

    if (! scopeObject)
        return;

    // global lookup for first part of id
    const Value *value = scopeObject->lookupMember(propertyName, &_context);
    if (_extraScope && !value)
        value = _extraScope->lookupMember(propertyName, &_context);
    if (!value) {
        error(id->identifierToken,
              QString("'%1' is not a valid property name").arg(propertyName));
        return;
    }

    // member lookup
    const UiQualifiedId *idPart = id;
    while (idPart->next) {
        const ObjectValue *objectValue = value_cast<const ObjectValue *>(value);
        if (! objectValue) {
            error(idPart->identifierToken,
                  QString("'%1' does not have members").arg(propertyName));
            return;
        }

        idPart = idPart->next;
        propertyName = idPart->name->asString();

        value = objectValue->lookupMember(propertyName, &_context);
        if (! value) {
            error(idPart->identifierToken,
                  QString("'%1' is not a member of '%2'").arg(propertyName, objectValue->className()));
            return;
        }
    }
}

void Check::error(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message));
}

void Check::warning(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message));
}
<|endoftext|>