<commit_before>/****************************************************************************
**
** Copyright (C) 2012 Denis Shienkov <scapig@yandex.ru>
** Copyright (C) 2012 Laszlo Papp <lpapp@kde.org>
** Copyright (C) 2012 Andre Hartmann <aha_1980@gmx.de>
** Contact: http://www.qt-project.org/
**
** This file is part of the QtSerialPort module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "serialport_win_p.h"

#include <QtCore/qelapsedtimer.h>

#include <QtCore/qthread.h>
#include <QtCore/qtimer.h>

QT_BEGIN_NAMESPACE_SERIALPORT

class SerialPortPrivate;

class CommEventNotifier : public QThread
{
    Q_OBJECT
signals:
    void eventMask(quint32 mask);

public:
    CommEventNotifier(DWORD mask, SerialPortPrivate *d, QObject *parent)
        : QThread(parent), dptr(d), running(true) {
        connect(this, SIGNAL(eventMask(quint32)), this, SLOT(processNotification(quint32)));
        ::SetCommMask(dptr->descriptor, mask);
    }

    virtual ~CommEventNotifier() {
        running = false;
        ::SetCommMask(dptr->descriptor, 0);
        wait();
    }

protected:
    virtual void run() {
        DWORD mask = 0;
        while (running) {
            if (::WaitCommEvent(dptr->descriptor, &mask, 0) != 0) {
                // Wait until complete the operation changes the port settings,
                // see updateDcb().
                dptr->settingsChangeMutex.lock();
                dptr->settingsChangeMutex.unlock();
                emit eventMask(quint32(mask));
            }
        }
    }

private slots:
    void processNotification(quint32 eventMask) {
        if (EV_ERR & eventMask)
            dptr->processIoErrors();
        if (EV_RXCHAR &eventMask)
            dptr->notifyRead();
        if (EV_TXEMPTY & eventMask)
            dptr->notifyWrite();
    }

private:
    SerialPortPrivate *dptr;
    mutable bool running;
};

class WaitCommEventBreaker : public QThread
{
    Q_OBJECT
public:
    WaitCommEventBreaker(HANDLE descriptor, int timeout, QObject *parent = 0)
        : QThread(parent), descriptor(descriptor), timeout(timeout), worked(false) {
        start();
    }

    virtual ~WaitCommEventBreaker() {
        stop();
        wait();
    }

    void stop() {
        exit(0);
    }

    bool isWorked() const {
        return worked;
    }

protected:
    void run() {
        QTimer timer;
        QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(processTimeout()), Qt::DirectConnection);
        timer.start(timeout);
        exec();
        worked = true;
    }

private slots:
    void processTimeout() {
        ::SetCommMask(descriptor, 0);
        stop();
    }

private:
    HANDLE descriptor;
    int timeout;
    mutable bool worked;
};

SerialPortPrivate::SerialPortPrivate(SerialPort *q)
    : SerialPortPrivateData(q)
    , descriptor(INVALID_HANDLE_VALUE)
    , flagErrorFromCommEvent(0)
    , eventNotifier(0)
{
}

bool SerialPortPrivate::open(QIODevice::OpenMode mode)
{
    DWORD desiredAccess = 0;
    DWORD eventMask = EV_ERR;

    if (mode & QIODevice::ReadOnly) {
        desiredAccess |= GENERIC_READ;
        eventMask |= EV_RXCHAR;
    }
    if (mode & QIODevice::WriteOnly) {
        desiredAccess |= GENERIC_WRITE;
        eventMask |= EV_TXEMPTY;
    }

    descriptor = ::CreateFile(reinterpret_cast<const wchar_t*>(systemLocation.utf16()),
                              desiredAccess, 0, 0, OPEN_EXISTING, 0, 0);

    if (descriptor == INVALID_HANDLE_VALUE) {
        portError = decodeSystemError();
        return false;
    }

    if (::GetCommState(descriptor, &restoredDcb) == 0) {
        portError = decodeSystemError();
        return false;
    }

    currentDcb = restoredDcb;
    currentDcb.fBinary = true;
    currentDcb.fInX = false;
    currentDcb.fOutX = false;
    currentDcb.fAbortOnError = false;
    currentDcb.fNull = false;
    currentDcb.fErrorChar = false;

    if (!updateDcb())
        return false;

    if (::GetCommTimeouts(descriptor, &restoredCommTimeouts) == 0) {
        portError = decodeSystemError();
        return false;
    }

    ::memset(&currentCommTimeouts, 0, sizeof(currentCommTimeouts));
    currentCommTimeouts.ReadIntervalTimeout = MAXDWORD;

    if (!updateCommTimeouts())
        return false;

    eventNotifier = new QtAddOn::SerialPort::CommEventNotifier(eventMask, this, q_ptr);
    eventNotifier->start();

    detectDefaultSettings();
    return true;
}

void SerialPortPrivate::close()
{
    if (eventNotifier) {
        eventNotifier->deleteLater();
        eventNotifier = 0;
    }

    if (restoreSettingsOnClose) {
        ::SetCommState(descriptor, &restoredDcb);
        ::SetCommTimeouts(descriptor, &restoredCommTimeouts);
    }

    ::CloseHandle(descriptor);
    descriptor = INVALID_HANDLE_VALUE;
}

qint64 SerialPortPrivate::readFromBuffer(char *data, qint64 maxSize)
{
    if (readBuffer.isEmpty())
        return 0;

    if (maxSize == 1) {
        *data = readBuffer.getChar();
        return 1;
    }

    const qint64 bytesToRead = qMin(qint64(readBuffer.size()), maxSize);
    qint64 readSoFar = 0;
    while (readSoFar < bytesToRead) {
        const char *ptr = readBuffer.readPointer();
        const int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar),
                                                  readBuffer.nextDataBlockSize());
        ::memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
        readSoFar += bytesToReadFromThisBlock;
        readBuffer.free(bytesToReadFromThisBlock);
    }

    return readSoFar;
}

qint64 SerialPortPrivate::writeToBuffer(const char *data, qint64 maxSize)
{
    char *ptr = writeBuffer.reserve(maxSize);
    if (maxSize == 1)
        *ptr = *data;
    else
        ::memcpy(ptr, data, maxSize);

    return maxSize;
}

bool SerialPortPrivate::waitForReadyRead(int msec)
{
    if (!readBuffer.isEmpty())
        return true;

    QElapsedTimer stopWatch;

    stopWatch.start();

    forever {
        bool readyToRead = false;
        bool readyToWrite = false;
        bool timedOut = false;
        if (!waitForReadOrWrite(&readyToRead, &readyToWrite,
                                true, !writeBuffer.isEmpty(),
                                timeoutValue(msec, stopWatch.elapsed()),
                                &timedOut)) {
            return false;
        }
        if (readyToRead) {
            if (notifyRead())
                return true;
        }
        if (readyToWrite)
            notifyWrite();
    }
    return false;
}

bool SerialPortPrivate::waitForBytesWritten(int msec)
{
    if (writeBuffer.isEmpty())
        return false;

    QElapsedTimer stopWatch;

    stopWatch.start();

    forever {
        bool readyToRead = false;
        bool readyToWrite = false;
        bool timedOut = false;
        if (!waitForReadOrWrite(&readyToRead, &readyToWrite,
                                true, !writeBuffer.isEmpty(),
                                timeoutValue(msec, stopWatch.elapsed()),
                                &timedOut)) {
            return false;
        }
        if (readyToRead) {
            if (!notifyRead())
                return false;
        }
        if (readyToWrite) {
            if (notifyWrite())
                return true;
        }
    }
    return false;
}

bool SerialPortPrivate::notifyRead()
{
    DWORD bytesToRead = (policy == SerialPort::IgnorePolicy) ? ReadChunkSize : 1;

    if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
        bytesToRead = readBufferMaxSize - readBuffer.size();
        if (bytesToRead == 0) {
            // Buffer is full. User must read data from the buffer
            // before we can read more from the port.
            return false;
        }
    }

    char *ptr = readBuffer.reserve(bytesToRead);

    DWORD readBytes = 0;
    bool sucessResult = ::ReadFile(descriptor, ptr, bytesToRead, &readBytes, 0);

    if (!sucessResult) {
        readBuffer.truncate(bytesToRead);
        return false;
    }

    readBuffer.truncate(readBytes);

    // Process emulate policy.
    if (flagErrorFromCommEvent) {
        flagErrorFromCommEvent = false;

        switch (policy) {
        case SerialPort::SkipPolicy:
            readBuffer.getChar();
            return true;
        case SerialPort::PassZeroPolicy:
            readBuffer.getChar();
            readBuffer.putChar('\0');
            break;
        case SerialPort::StopReceivingPolicy:
            // FIXME: Maybe need disable read notifier?
            break;
        default:
            break;
        }
    }

    if (readBytes > 0)
        emit q_ptr->readyRead();

    return true;
}

bool SerialPortPrivate::notifyWrite()
{
    const DWORD nextSize = qMin(writeBuffer.nextDataBlockSize(), int(WriteChunkSize));
    const char *ptr = writeBuffer.readPointer();

    DWORD bytesWritten = 0;
    bool sucessResult = ::WriteFile(descriptor, ptr, nextSize, &bytesWritten, 0);
    if (!sucessResult)
        return false;

    if (sucessResult)
        writeBuffer.free(bytesWritten);

    if (bytesWritten > 0)
        emit q_ptr->bytesWritten(bytesWritten);

    return true;
}

bool SerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite,
                                           bool checkRead, bool checkWrite,
                                           int msecs, bool *timedOut)
{
    // FIXME: Here the situation is not properly handled with zero timeout:
    // breaker can work out before you call a method WaitCommEvent()
    // and so it will loop forever!
    WaitCommEventBreaker breaker(descriptor, qMax(msecs, 0));
    ::WaitCommEvent(descriptor, &eventMask, 0);
    breaker.stop();

    if (breaker.isWorked())
        *timedOut = true;

    if (!breaker.isWorked()) {
        if (checkRead) {
            Q_ASSERT(selectForRead);
            *selectForRead = eventMask & EV_RXCHAR;
        }
        if (checkWrite) {
            Q_ASSERT(selectForWrite);
            *selectForWrite = eventMask & EV_TXEMPTY;
        }

        return true;
    }

    return false;
}

bool SerialPortPrivate::updateDcb()
{
    QMutexLocker locker(&settingsChangeMutex);

    DWORD eventMask = 0;
    // Save the event mask
    if (::GetCommMask(descriptor, &eventMask) == 0)
        return false;

    // Break event notifier from WaitCommEvent
    ::SetCommMask(descriptor, 0);
    // Change parameters
    bool ret = (::SetCommState(descriptor, &currentDcb) != 0);
    if (!ret)
        portError = decodeSystemError();
    // Restore the event mask
    ::SetCommMask(descriptor, eventMask);

    return ret;
}

bool SerialPortPrivate::updateCommTimeouts()
{
    if (::SetCommTimeouts(descriptor, &currentCommTimeouts) == 0) {
        portError = decodeSystemError();
        return false;
    }
    return true;
}

static const QLatin1String defaultPathPostfix(":");

QString SerialPortPrivate::portNameToSystemLocation(const QString &port)
{
    QString ret = port;
    if (!ret.contains(defaultPathPostfix))
        ret.append(defaultPathPostfix);
    return ret;
}

QString SerialPortPrivate::portNameFromSystemLocation(const QString &location)
{
    QString ret = location;
    if (ret.contains(defaultPathPostfix))
        ret.remove(defaultPathPostfix);
    return ret;
}

#include "serialport_wince.moc"

QT_END_NAMESPACE_SERIALPORT
<commit_msg>Eliminate the unnecessary variable and condition check<commit_after>/****************************************************************************
**
** Copyright (C) 2012 Denis Shienkov <scapig@yandex.ru>
** Copyright (C) 2012 Laszlo Papp <lpapp@kde.org>
** Copyright (C) 2012 Andre Hartmann <aha_1980@gmx.de>
** Contact: http://www.qt-project.org/
**
** This file is part of the QtSerialPort module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "serialport_win_p.h"

#include <QtCore/qelapsedtimer.h>

#include <QtCore/qthread.h>
#include <QtCore/qtimer.h>

QT_BEGIN_NAMESPACE_SERIALPORT

class SerialPortPrivate;

class CommEventNotifier : public QThread
{
    Q_OBJECT
signals:
    void eventMask(quint32 mask);

public:
    CommEventNotifier(DWORD mask, SerialPortPrivate *d, QObject *parent)
        : QThread(parent), dptr(d), running(true) {
        connect(this, SIGNAL(eventMask(quint32)), this, SLOT(processNotification(quint32)));
        ::SetCommMask(dptr->descriptor, mask);
    }

    virtual ~CommEventNotifier() {
        running = false;
        ::SetCommMask(dptr->descriptor, 0);
        wait();
    }

protected:
    virtual void run() {
        DWORD mask = 0;
        while (running) {
            if (::WaitCommEvent(dptr->descriptor, &mask, 0) != 0) {
                // Wait until complete the operation changes the port settings,
                // see updateDcb().
                dptr->settingsChangeMutex.lock();
                dptr->settingsChangeMutex.unlock();
                emit eventMask(quint32(mask));
            }
        }
    }

private slots:
    void processNotification(quint32 eventMask) {
        if (EV_ERR & eventMask)
            dptr->processIoErrors();
        if (EV_RXCHAR &eventMask)
            dptr->notifyRead();
        if (EV_TXEMPTY & eventMask)
            dptr->notifyWrite();
    }

private:
    SerialPortPrivate *dptr;
    mutable bool running;
};

class WaitCommEventBreaker : public QThread
{
    Q_OBJECT
public:
    WaitCommEventBreaker(HANDLE descriptor, int timeout, QObject *parent = 0)
        : QThread(parent), descriptor(descriptor), timeout(timeout), worked(false) {
        start();
    }

    virtual ~WaitCommEventBreaker() {
        stop();
        wait();
    }

    void stop() {
        exit(0);
    }

    bool isWorked() const {
        return worked;
    }

protected:
    void run() {
        QTimer timer;
        QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(processTimeout()), Qt::DirectConnection);
        timer.start(timeout);
        exec();
        worked = true;
    }

private slots:
    void processTimeout() {
        ::SetCommMask(descriptor, 0);
        stop();
    }

private:
    HANDLE descriptor;
    int timeout;
    mutable bool worked;
};

SerialPortPrivate::SerialPortPrivate(SerialPort *q)
    : SerialPortPrivateData(q)
    , descriptor(INVALID_HANDLE_VALUE)
    , flagErrorFromCommEvent(0)
    , eventNotifier(0)
{
}

bool SerialPortPrivate::open(QIODevice::OpenMode mode)
{
    DWORD desiredAccess = 0;
    DWORD eventMask = EV_ERR;

    if (mode & QIODevice::ReadOnly) {
        desiredAccess |= GENERIC_READ;
        eventMask |= EV_RXCHAR;
    }
    if (mode & QIODevice::WriteOnly) {
        desiredAccess |= GENERIC_WRITE;
        eventMask |= EV_TXEMPTY;
    }

    descriptor = ::CreateFile(reinterpret_cast<const wchar_t*>(systemLocation.utf16()),
                              desiredAccess, 0, 0, OPEN_EXISTING, 0, 0);

    if (descriptor == INVALID_HANDLE_VALUE) {
        portError = decodeSystemError();
        return false;
    }

    if (::GetCommState(descriptor, &restoredDcb) == 0) {
        portError = decodeSystemError();
        return false;
    }

    currentDcb = restoredDcb;
    currentDcb.fBinary = true;
    currentDcb.fInX = false;
    currentDcb.fOutX = false;
    currentDcb.fAbortOnError = false;
    currentDcb.fNull = false;
    currentDcb.fErrorChar = false;

    if (!updateDcb())
        return false;

    if (::GetCommTimeouts(descriptor, &restoredCommTimeouts) == 0) {
        portError = decodeSystemError();
        return false;
    }

    ::memset(&currentCommTimeouts, 0, sizeof(currentCommTimeouts));
    currentCommTimeouts.ReadIntervalTimeout = MAXDWORD;

    if (!updateCommTimeouts())
        return false;

    eventNotifier = new QtAddOn::SerialPort::CommEventNotifier(eventMask, this, q_ptr);
    eventNotifier->start();

    detectDefaultSettings();
    return true;
}

void SerialPortPrivate::close()
{
    if (eventNotifier) {
        eventNotifier->deleteLater();
        eventNotifier = 0;
    }

    if (restoreSettingsOnClose) {
        ::SetCommState(descriptor, &restoredDcb);
        ::SetCommTimeouts(descriptor, &restoredCommTimeouts);
    }

    ::CloseHandle(descriptor);
    descriptor = INVALID_HANDLE_VALUE;
}

qint64 SerialPortPrivate::readFromBuffer(char *data, qint64 maxSize)
{
    if (readBuffer.isEmpty())
        return 0;

    if (maxSize == 1) {
        *data = readBuffer.getChar();
        return 1;
    }

    const qint64 bytesToRead = qMin(qint64(readBuffer.size()), maxSize);
    qint64 readSoFar = 0;
    while (readSoFar < bytesToRead) {
        const char *ptr = readBuffer.readPointer();
        const int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar),
                                                  readBuffer.nextDataBlockSize());
        ::memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
        readSoFar += bytesToReadFromThisBlock;
        readBuffer.free(bytesToReadFromThisBlock);
    }

    return readSoFar;
}

qint64 SerialPortPrivate::writeToBuffer(const char *data, qint64 maxSize)
{
    char *ptr = writeBuffer.reserve(maxSize);
    if (maxSize == 1)
        *ptr = *data;
    else
        ::memcpy(ptr, data, maxSize);

    return maxSize;
}

bool SerialPortPrivate::waitForReadyRead(int msec)
{
    if (!readBuffer.isEmpty())
        return true;

    QElapsedTimer stopWatch;

    stopWatch.start();

    forever {
        bool readyToRead = false;
        bool readyToWrite = false;
        bool timedOut = false;
        if (!waitForReadOrWrite(&readyToRead, &readyToWrite,
                                true, !writeBuffer.isEmpty(),
                                timeoutValue(msec, stopWatch.elapsed()),
                                &timedOut)) {
            return false;
        }
        if (readyToRead) {
            if (notifyRead())
                return true;
        }
        if (readyToWrite)
            notifyWrite();
    }
    return false;
}

bool SerialPortPrivate::waitForBytesWritten(int msec)
{
    if (writeBuffer.isEmpty())
        return false;

    QElapsedTimer stopWatch;

    stopWatch.start();

    forever {
        bool readyToRead = false;
        bool readyToWrite = false;
        bool timedOut = false;
        if (!waitForReadOrWrite(&readyToRead, &readyToWrite,
                                true, !writeBuffer.isEmpty(),
                                timeoutValue(msec, stopWatch.elapsed()),
                                &timedOut)) {
            return false;
        }
        if (readyToRead) {
            if (!notifyRead())
                return false;
        }
        if (readyToWrite) {
            if (notifyWrite())
                return true;
        }
    }
    return false;
}

bool SerialPortPrivate::notifyRead()
{
    DWORD bytesToRead = (policy == SerialPort::IgnorePolicy) ? ReadChunkSize : 1;

    if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
        bytesToRead = readBufferMaxSize - readBuffer.size();
        if (bytesToRead == 0) {
            // Buffer is full. User must read data from the buffer
            // before we can read more from the port.
            return false;
        }
    }

    char *ptr = readBuffer.reserve(bytesToRead);

    DWORD readBytes = 0;
    bool sucessResult = ::ReadFile(descriptor, ptr, bytesToRead, &readBytes, 0);

    if (!sucessResult) {
        readBuffer.truncate(bytesToRead);
        return false;
    }

    readBuffer.truncate(readBytes);

    // Process emulate policy.
    if (flagErrorFromCommEvent) {
        flagErrorFromCommEvent = false;

        switch (policy) {
        case SerialPort::SkipPolicy:
            readBuffer.getChar();
            return true;
        case SerialPort::PassZeroPolicy:
            readBuffer.getChar();
            readBuffer.putChar('\0');
            break;
        case SerialPort::StopReceivingPolicy:
            // FIXME: Maybe need disable read notifier?
            break;
        default:
            break;
        }
    }

    if (readBytes > 0)
        emit q_ptr->readyRead();

    return true;
}

bool SerialPortPrivate::notifyWrite()
{
    const DWORD nextSize = qMin(writeBuffer.nextDataBlockSize(), int(WriteChunkSize));
    const char *ptr = writeBuffer.readPointer();

    DWORD bytesWritten = 0;
    if (::WriteFile(descriptor, ptr, nextSize, &bytesWritten, 0) == 0)
        return false;

    writeBuffer.free(bytesWritten);

    if (bytesWritten > 0)
        emit q_ptr->bytesWritten(bytesWritten);

    return true;
}

bool SerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite,
                                           bool checkRead, bool checkWrite,
                                           int msecs, bool *timedOut)
{
    // FIXME: Here the situation is not properly handled with zero timeout:
    // breaker can work out before you call a method WaitCommEvent()
    // and so it will loop forever!
    WaitCommEventBreaker breaker(descriptor, qMax(msecs, 0));
    ::WaitCommEvent(descriptor, &eventMask, 0);
    breaker.stop();

    if (breaker.isWorked())
        *timedOut = true;

    if (!breaker.isWorked()) {
        if (checkRead) {
            Q_ASSERT(selectForRead);
            *selectForRead = eventMask & EV_RXCHAR;
        }
        if (checkWrite) {
            Q_ASSERT(selectForWrite);
            *selectForWrite = eventMask & EV_TXEMPTY;
        }

        return true;
    }

    return false;
}

bool SerialPortPrivate::updateDcb()
{
    QMutexLocker locker(&settingsChangeMutex);

    DWORD eventMask = 0;
    // Save the event mask
    if (::GetCommMask(descriptor, &eventMask) == 0)
        return false;

    // Break event notifier from WaitCommEvent
    ::SetCommMask(descriptor, 0);
    // Change parameters
    bool ret = (::SetCommState(descriptor, &currentDcb) != 0);
    if (!ret)
        portError = decodeSystemError();
    // Restore the event mask
    ::SetCommMask(descriptor, eventMask);

    return ret;
}

bool SerialPortPrivate::updateCommTimeouts()
{
    if (::SetCommTimeouts(descriptor, &currentCommTimeouts) == 0) {
        portError = decodeSystemError();
        return false;
    }
    return true;
}

static const QLatin1String defaultPathPostfix(":");

QString SerialPortPrivate::portNameToSystemLocation(const QString &port)
{
    QString ret = port;
    if (!ret.contains(defaultPathPostfix))
        ret.append(defaultPathPostfix);
    return ret;
}

QString SerialPortPrivate::portNameFromSystemLocation(const QString &location)
{
    QString ret = location;
    if (ret.contains(defaultPathPostfix))
        ret.remove(defaultPathPostfix);
    return ret;
}

#include "serialport_wince.moc"

QT_END_NAMESPACE_SERIALPORT
<|endoftext|>


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

#include "chrome/browser/chromeos/cros/burn_library.h"

#include "base/bind.h"
#include "base/observer_list.h"
#include "chrome/browser/chromeos/dbus/dbus_thread_manager.h"
#include "chrome/browser/chromeos/dbus/image_burner_client.h"
#include "chrome/browser/chromeos/disks/disk_mount_manager.h"
#include "chrome/common/zip.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace chromeos {

namespace {

// Unzips |source_zip_file| and calls |callback| with the filename of
// the unzipped image.
void UnzipImage(
    const FilePath& source_zip_file,
    const std::string& image_name,
    base::Callback<void(const std::string& source_image_file)> callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  std::string source_image_file;
  if (zip::Unzip(source_zip_file, source_zip_file.DirName())) {
    source_image_file =
        source_zip_file.DirName().Append(image_name).value();
  }
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE, base::Bind(callback, source_image_file));
}

class BurnLibraryImpl : public BurnLibrary {
 public:
  BurnLibraryImpl();
  virtual ~BurnLibraryImpl();

  // BurnLibrary implementation.
  virtual void AddObserver(Observer* observer) OVERRIDE;
  virtual void RemoveObserver(Observer* observer) OVERRIDE;
  virtual void DoBurn(const FilePath& source_path,
                      const std::string& image_name,
                      const FilePath& target_file_path,
                      const FilePath& target_device_path) OVERRIDE;
  virtual void CancelBurnImage() OVERRIDE;

  void OnImageUnzipped(const std::string& source_image_file);

 private:
  void Init();
  void BurnImage();
  static void DevicesUnmountedCallback(void* object, bool success);
  void UpdateBurnStatus(const ImageBurnStatus& status, BurnEvent evt);
  void OnBurnFinished(const std::string& target_path,
                      bool success,
                      const std::string& error);
  void OnBurnProgressUpdate(const std::string& target_path,
                            int64 num_bytes_burnt,
                            int64 total_size);
  void OnBurnImageFail();

  base::WeakPtrFactory<BurnLibraryImpl> weak_ptr_factory_;

  ObserverList<BurnLibrary::Observer> observers_;

  std::string source_image_file_;
  std::string target_file_path_;
  std::string target_device_path_;

  bool unzipping_;
  bool cancelled_;
  bool burning_;
  bool block_burn_signals_;

  DISALLOW_COPY_AND_ASSIGN(BurnLibraryImpl);
};

BurnLibraryImpl::BurnLibraryImpl()
    : weak_ptr_factory_(this),
      unzipping_(false),
      cancelled_(false),
      burning_(false),
      block_burn_signals_(false) {
}

BurnLibraryImpl::~BurnLibraryImpl() {
  DBusThreadManager::Get()->GetImageBurnerClient()->ResetEventHandlers();
}

void BurnLibraryImpl::Init() {
  DBusThreadManager::Get()->GetImageBurnerClient()->SetEventHandlers(
      base::Bind(&BurnLibraryImpl::OnBurnFinished,
                 weak_ptr_factory_.GetWeakPtr()),
      base::Bind(&BurnLibraryImpl::OnBurnProgressUpdate,
                 weak_ptr_factory_.GetWeakPtr()));
}

void BurnLibraryImpl::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
  if (unzipping_ && !cancelled_)
    UpdateBurnStatus(ImageBurnStatus(), UNZIP_STARTED);
}

void BurnLibraryImpl::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

void BurnLibraryImpl::CancelBurnImage() {
  // At the moment, we cannot really stop uzipping or burning. Instead we
  // prevent events from being send to listeners.
  if (burning_)
    block_burn_signals_ = true;
  cancelled_ = true;
}

void BurnLibraryImpl::DoBurn(const FilePath& source_path,
    const std::string& image_name,
    const FilePath& target_file_path,
    const FilePath& target_device_path) {
  if (unzipping_) {
    // We have unzip in progress, maybe it was "cancelled" before and did not
    // finish yet. In that case, let's pretend cancel did not happen.
    cancelled_ = false;
    UpdateBurnStatus(ImageBurnStatus(), UNZIP_STARTED);
    return;
  }

  source_image_file_.clear();
  target_file_path_ = target_file_path.value();
  target_device_path_ = target_device_path.value();

  unzipping_ = true;
  cancelled_ = false;
  UpdateBurnStatus(ImageBurnStatus(), UNZIP_STARTED);

  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(UnzipImage,
                 source_path,
                 image_name,
                 base::Bind(&BurnLibraryImpl::OnImageUnzipped,
                            weak_ptr_factory_.GetWeakPtr())));
}

void BurnLibraryImpl::OnImageUnzipped(const std::string& source_image_file) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  source_image_file_ = source_image_file;

  bool success = !source_image_file_.empty();
  UpdateBurnStatus(ImageBurnStatus(), (success ? UNZIP_COMPLETE : UNZIP_FAIL));

  unzipping_ = false;
  if (cancelled_) {
    cancelled_ = false;
    return;
  }

  if (!success)
    return;

  burning_ = true;

  chromeos::disks::DiskMountManager::GetInstance()->
      UnmountDeviceRecursive(target_device_path_,
                             &BurnLibraryImpl::DevicesUnmountedCallback,
                             this);
}

void BurnLibraryImpl::OnBurnFinished(const std::string& target_path,
                                     bool success,
                                     const std::string& error) {
  UpdateBurnStatus(ImageBurnStatus(0, 0), success ? BURN_SUCCESS : BURN_FAIL);
}

void BurnLibraryImpl::OnBurnProgressUpdate(const std::string& target_path,
                                           int64 amount_burnt,
                                           int64 total_size) {
  UpdateBurnStatus(ImageBurnStatus(amount_burnt, total_size), BURN_UPDATE);
}

void BurnLibraryImpl::OnBurnImageFail() {
  UpdateBurnStatus(ImageBurnStatus(0, 0), BURN_FAIL);
}

void BurnLibraryImpl::DevicesUnmountedCallback(void* object, bool success) {
  DCHECK(object);
  BurnLibraryImpl* self = static_cast<BurnLibraryImpl*>(object);
  if (success) {
    self->BurnImage();
  } else {
    self->UpdateBurnStatus(ImageBurnStatus(0, 0), BURN_FAIL);
  }
}

void BurnLibraryImpl::BurnImage() {
  DBusThreadManager::Get()->GetImageBurnerClient()->BurnImage(
      source_image_file_,
      target_file_path_,
      base::Bind(&BurnLibraryImpl::OnBurnImageFail,
                 weak_ptr_factory_.GetWeakPtr()));
}

void BurnLibraryImpl::UpdateBurnStatus(const ImageBurnStatus& status,
                                       BurnEvent evt) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (cancelled_)
    return;

  if (evt == BURN_FAIL || evt == BURN_SUCCESS) {
    burning_ = false;
    if (block_burn_signals_) {
      block_burn_signals_ = false;
      return;
    }
  }

  if (block_burn_signals_ && evt == BURN_UPDATE)
    return;

  FOR_EACH_OBSERVER(Observer, observers_,
      BurnProgressUpdated(this, evt, status));
}

class BurnLibraryStubImpl : public BurnLibrary {
 public:
  BurnLibraryStubImpl() {}
  virtual ~BurnLibraryStubImpl() {}

  // BurnLibrary overrides.
  virtual void Init() OVERRIDE {}
  virtual void AddObserver(Observer* observer) OVERRIDE {}
  virtual void RemoveObserver(Observer* observer) OVERRIDE {}
  virtual void DoBurn(const FilePath& source_path,
                      const std::string& image_name,
                      const FilePath& target_file_path,
                      const FilePath& target_device_path) OVERRIDE {
  }
  virtual void CancelBurnImage() OVERRIDE {}

  DISALLOW_COPY_AND_ASSIGN(BurnLibraryStubImpl);
};

}  // namespace

// static
BurnLibrary* BurnLibrary::GetImpl(bool stub) {
  BurnLibrary* impl;
  if (stub)
    impl = new BurnLibraryStubImpl();
  else
    impl = new BurnLibraryImpl();
  impl->Init();
  return impl;
}

}  // namespace chromeos
<commit_msg>Use WorkerPool for unzipping recovery image to avoid blocking FILE thread for long time.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/cros/burn_library.h"

#include "base/bind.h"
#include "base/location.h"
#include "base/memory/ref_counted_memory.h"
#include "base/observer_list.h"
#include "base/threading/worker_pool.h"
#include "chrome/browser/chromeos/dbus/dbus_thread_manager.h"
#include "chrome/browser/chromeos/dbus/image_burner_client.h"
#include "chrome/browser/chromeos/disks/disk_mount_manager.h"
#include "chrome/common/zip.h"

namespace chromeos {

namespace {

// Unzips |source_zip_file| and sets the filename of the unzipped image to
// |source_image_file|.
void UnzipImage(const FilePath& source_zip_file,
                const std::string& image_name,
                scoped_refptr<base::RefCountedString> source_image_file) {
  if (zip::Unzip(source_zip_file, source_zip_file.DirName())) {
    source_image_file->data() =
        source_zip_file.DirName().Append(image_name).value();
  }
}

class BurnLibraryImpl : public BurnLibrary {
 public:
  BurnLibraryImpl();
  virtual ~BurnLibraryImpl();

  // BurnLibrary implementation.
  virtual void AddObserver(Observer* observer) OVERRIDE;
  virtual void RemoveObserver(Observer* observer) OVERRIDE;
  virtual void DoBurn(const FilePath& source_path,
                      const std::string& image_name,
                      const FilePath& target_file_path,
                      const FilePath& target_device_path) OVERRIDE;
  virtual void CancelBurnImage() OVERRIDE;

  void OnImageUnzipped(scoped_refptr<base::RefCountedString> source_image_file);

 private:
  void Init();
  void BurnImage();
  static void DevicesUnmountedCallback(void* object, bool success);
  void UpdateBurnStatus(const ImageBurnStatus& status, BurnEvent evt);
  void OnBurnFinished(const std::string& target_path,
                      bool success,
                      const std::string& error);
  void OnBurnProgressUpdate(const std::string& target_path,
                            int64 num_bytes_burnt,
                            int64 total_size);
  void OnBurnImageFail();

  base::WeakPtrFactory<BurnLibraryImpl> weak_ptr_factory_;

  ObserverList<BurnLibrary::Observer> observers_;

  std::string source_image_file_;
  std::string target_file_path_;
  std::string target_device_path_;

  bool unzipping_;
  bool cancelled_;
  bool burning_;
  bool block_burn_signals_;

  DISALLOW_COPY_AND_ASSIGN(BurnLibraryImpl);
};

BurnLibraryImpl::BurnLibraryImpl()
    : weak_ptr_factory_(this),
      unzipping_(false),
      cancelled_(false),
      burning_(false),
      block_burn_signals_(false) {
}

BurnLibraryImpl::~BurnLibraryImpl() {
  DBusThreadManager::Get()->GetImageBurnerClient()->ResetEventHandlers();
}

void BurnLibraryImpl::Init() {
  DBusThreadManager::Get()->GetImageBurnerClient()->SetEventHandlers(
      base::Bind(&BurnLibraryImpl::OnBurnFinished,
                 weak_ptr_factory_.GetWeakPtr()),
      base::Bind(&BurnLibraryImpl::OnBurnProgressUpdate,
                 weak_ptr_factory_.GetWeakPtr()));
}

void BurnLibraryImpl::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
  if (unzipping_ && !cancelled_)
    UpdateBurnStatus(ImageBurnStatus(), UNZIP_STARTED);
}

void BurnLibraryImpl::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

void BurnLibraryImpl::CancelBurnImage() {
  // At the moment, we cannot really stop uzipping or burning. Instead we
  // prevent events from being send to listeners.
  if (burning_)
    block_burn_signals_ = true;
  cancelled_ = true;
}

void BurnLibraryImpl::DoBurn(const FilePath& source_path,
    const std::string& image_name,
    const FilePath& target_file_path,
    const FilePath& target_device_path) {
  if (unzipping_) {
    // We have unzip in progress, maybe it was "cancelled" before and did not
    // finish yet. In that case, let's pretend cancel did not happen.
    cancelled_ = false;
    UpdateBurnStatus(ImageBurnStatus(), UNZIP_STARTED);
    return;
  }

  source_image_file_.clear();
  target_file_path_ = target_file_path.value();
  target_device_path_ = target_device_path.value();

  unzipping_ = true;
  cancelled_ = false;
  UpdateBurnStatus(ImageBurnStatus(), UNZIP_STARTED);

  const bool task_is_slow = true;
  scoped_refptr<base::RefCountedString> result(new base::RefCountedString);
  base::WorkerPool::PostTaskAndReply(
      FROM_HERE,
      base::Bind(UnzipImage, source_path, image_name, result),
      base::Bind(&BurnLibraryImpl::OnImageUnzipped,
                 weak_ptr_factory_.GetWeakPtr(),
                 result),
      task_is_slow);
}

void BurnLibraryImpl::OnImageUnzipped(
    scoped_refptr<base::RefCountedString> source_image_file) {
  source_image_file_ = source_image_file->data();

  bool success = !source_image_file_.empty();
  UpdateBurnStatus(ImageBurnStatus(), (success ? UNZIP_COMPLETE : UNZIP_FAIL));

  unzipping_ = false;
  if (cancelled_) {
    cancelled_ = false;
    return;
  }

  if (!success)
    return;

  burning_ = true;

  chromeos::disks::DiskMountManager::GetInstance()->
      UnmountDeviceRecursive(target_device_path_,
                             &BurnLibraryImpl::DevicesUnmountedCallback,
                             this);
}

void BurnLibraryImpl::OnBurnFinished(const std::string& target_path,
                                     bool success,
                                     const std::string& error) {
  UpdateBurnStatus(ImageBurnStatus(0, 0), success ? BURN_SUCCESS : BURN_FAIL);
}

void BurnLibraryImpl::OnBurnProgressUpdate(const std::string& target_path,
                                           int64 amount_burnt,
                                           int64 total_size) {
  UpdateBurnStatus(ImageBurnStatus(amount_burnt, total_size), BURN_UPDATE);
}

void BurnLibraryImpl::OnBurnImageFail() {
  UpdateBurnStatus(ImageBurnStatus(0, 0), BURN_FAIL);
}

void BurnLibraryImpl::DevicesUnmountedCallback(void* object, bool success) {
  DCHECK(object);
  BurnLibraryImpl* self = static_cast<BurnLibraryImpl*>(object);
  if (success) {
    self->BurnImage();
  } else {
    self->UpdateBurnStatus(ImageBurnStatus(0, 0), BURN_FAIL);
  }
}

void BurnLibraryImpl::BurnImage() {
  DBusThreadManager::Get()->GetImageBurnerClient()->BurnImage(
      source_image_file_,
      target_file_path_,
      base::Bind(&BurnLibraryImpl::OnBurnImageFail,
                 weak_ptr_factory_.GetWeakPtr()));
}

void BurnLibraryImpl::UpdateBurnStatus(const ImageBurnStatus& status,
                                       BurnEvent evt) {
  if (cancelled_)
    return;

  if (evt == BURN_FAIL || evt == BURN_SUCCESS) {
    burning_ = false;
    if (block_burn_signals_) {
      block_burn_signals_ = false;
      return;
    }
  }

  if (block_burn_signals_ && evt == BURN_UPDATE)
    return;

  FOR_EACH_OBSERVER(Observer, observers_,
      BurnProgressUpdated(this, evt, status));
}

class BurnLibraryStubImpl : public BurnLibrary {
 public:
  BurnLibraryStubImpl() {}
  virtual ~BurnLibraryStubImpl() {}

  // BurnLibrary overrides.
  virtual void Init() OVERRIDE {}
  virtual void AddObserver(Observer* observer) OVERRIDE {}
  virtual void RemoveObserver(Observer* observer) OVERRIDE {}
  virtual void DoBurn(const FilePath& source_path,
                      const std::string& image_name,
                      const FilePath& target_file_path,
                      const FilePath& target_device_path) OVERRIDE {
  }
  virtual void CancelBurnImage() OVERRIDE {}

  DISALLOW_COPY_AND_ASSIGN(BurnLibraryStubImpl);
};

}  // namespace

// static
BurnLibrary* BurnLibrary::GetImpl(bool stub) {
  BurnLibrary* impl;
  if (stub)
    impl = new BurnLibraryStubImpl();
  else
    impl = new BurnLibraryImpl();
  impl->Init();
  return impl;
}

}  // namespace chromeos
<|endoftext|>


<commit_before>/*
  This file is part of Ingen.
  Copyright 2007-2012 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen 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 Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cstdlib>
#include <cassert>

#include "ingen/Patch.hpp"

#include "AudioBuffer.hpp"
#include "BufferFactory.hpp"
#include "EdgeImpl.hpp"
#include "Engine.hpp"
#include "InputPort.hpp"
#include "NodeImpl.hpp"
#include "Notification.hpp"
#include "OutputPort.hpp"
#include "ProcessContext.hpp"
#include "ThreadManager.hpp"
#include "ingen/shared/URIs.hpp"
#include "mix.hpp"
#include "util.hpp"

using namespace std;

namespace Ingen {
namespace Server {

InputPort::InputPort(BufferFactory&      bufs,
                     NodeImpl*           parent,
                     const Raul::Symbol& symbol,
                     uint32_t            index,
                     uint32_t            poly,
                     PortType            type,
                     LV2_URID            buffer_type,
                     const Raul::Atom&   value,
                     size_t              buffer_size)
	: PortImpl(bufs, parent, symbol, index, poly, type, buffer_type, value, buffer_size)
	, _num_edges(0)
{
	const Ingen::Shared::URIs& uris = bufs.uris();

	if (!dynamic_cast<Patch*>(parent))
		add_property(uris.rdf_type, uris.lv2_InputPort);

	// Set default control range
	if (type == PortType::CONTROL || type == PortType::CV) {
		set_property(uris.lv2_minimum, bufs.forge().make(0.0f));
		set_property(uris.lv2_maximum, bufs.forge().make(1.0f));
	}
}

bool
InputPort::apply_poly(ProcessContext& context, Raul::Maid& maid, uint32_t poly)
{
	bool ret = PortImpl::apply_poly(context, maid, poly);
	if (!ret)
		poly = 1;

	assert(_buffers->size() >= poly);

	return true;
}

/** Set @a buffers to the buffers to be used for this port.
 * @return true iff buffers are locally owned by the port
 */
bool
InputPort::get_buffers(Context&                context,
                       BufferFactory&          bufs,
                       Raul::Array<BufferRef>* buffers,
                       uint32_t                poly) const
{
	const bool is_process_context = bufs.engine().is_process_context(context);
	size_t num_edges = is_process_context ? _edges.size() : _num_edges;

	if (is_a(PortType::AUDIO) && num_edges == 0) {
		// Audio input with no edges, use shared zero buffer
		for (uint32_t v = 0; v < poly; ++v)
			buffers->at(v) = bufs.silent_buffer();
		return false;

	} else if (num_edges == 1) {
		if (is_process_context) {
			if (!_edges.front().must_mix() &&
			    !_edges.front().must_queue()) {
				// Single non-mixing connection, use buffers directly
				for (uint32_t v = 0; v < poly; ++v)
					buffers->at(v) = _edges.front().buffer(v);
				return false;
			}
		}
	}

	// Otherwise, allocate local buffers
	for (uint32_t v = 0; v < poly; ++v) {
		buffers->at(v) = _bufs.get(context, buffer_type(), _buffer_size);
		buffers->at(v)->clear();
	}
	return true;
}

/** Add a edge.  Realtime safe.
 *
 * The buffer of this port will be set directly to the edge's buffer
 * if there is only one edge, since no copying/mixing needs to take place.
 *
 * Note that setup_buffers must be called after this before the change
 * will audibly take effect.
 */
void
InputPort::add_edge(ProcessContext& context, EdgeImpl* c)
{
	_edges.push_front(*c);
	_broadcast = true;	// Broadcast value/activity of connected input
}

/** Remove a edge.  Realtime safe.
 *
 * Note that setup_buffers must be called after this before the change
 * will audibly take effect.
 */
EdgeImpl*
InputPort::remove_edge(ProcessContext& context, const OutputPort* tail)
{
	EdgeImpl* edge = NULL;
	for (Edges::iterator i = _edges.begin(); i != _edges.end(); ++i) {
		if (i->tail() == tail) {
			edge = &*i;
			_edges.erase(i);
			break;
		}
	}

	if (!edge) {
		Raul::error << "[InputPort::remove_edge] Edge not found!"
		            << std::endl;
		return NULL;
	}

	// Turn off broadcasting if we're no longer connected
	if (_edges.empty()) {
		if (is_a(PortType::AUDIO)) {
			// Send an update peak of 0.0 to reset to silence
			const Notification note = Notification::make(
				Notification::PORT_ACTIVITY, context.start(), this,
				context.engine().world()->forge().make(0.0f));
			context.event_sink().write(sizeof(note), &note);
		}
		_broadcast = false;
	}

	return edge;
}

/** Prepare buffer for access, mixing if necessary.  Realtime safe.
 */
void
InputPort::pre_process(Context& context)
{
	// If value has been set (e.g. events pushed) by the user, don't smash it
	if (_set_by_user)
		return;

	if (_edges.empty()) {
		for (uint32_t v = 0; v < _poly; ++v) {
			buffer(v)->prepare_read(context);
		}
	} else if (direct_connect()) {
		for (uint32_t v = 0; v < _poly; ++v) {
			_buffers->at(v) = _edges.front().buffer(v);
			_buffers->at(v)->prepare_read(context);
		}
	} else {
		uint32_t max_num_srcs = 0;
		for (Edges::const_iterator e = _edges.begin(); e != _edges.end(); ++e) {
			max_num_srcs += e->tail()->poly();
		}

		Buffer* srcs[max_num_srcs];
		for (uint32_t v = 0; v < _poly; ++v) {
			uint32_t num_srcs = 0;
			for (Edges::iterator e = _edges.begin(); e != _edges.end(); ++e) {
				e->get_sources(context, v, srcs, max_num_srcs, num_srcs);
			}

			mix(context, bufs().uris(), buffer(v).get(), srcs, num_srcs);
			buffer(v)->prepare_read(context);
		}
	}

	if (_broadcast)
		broadcast_value(context, false);
}

void
InputPort::post_process(Context& context)
{
	if (_set_by_user) {
		if (_buffer_type == _bufs.uris().atom_Sequence) {
			// Clear events received via a SetPortValue
			for (uint32_t v = 0; v < _poly; ++v) {
				buffer(v)->clear();
			}
		}
		_set_by_user = false;
	}
}

bool
InputPort::direct_connect() const
{
	return _edges.size() == 1
		&& !_edges.front().must_mix()
		&& !_edges.front().must_queue();
}

} // namespace Server
} // namespace Ingen

<commit_msg>Avoid possibly creating a variable length array of size 0.<commit_after>/*
  This file is part of Ingen.
  Copyright 2007-2012 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen 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 Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cstdlib>
#include <cassert>

#include "ingen/Patch.hpp"

#include "AudioBuffer.hpp"
#include "BufferFactory.hpp"
#include "EdgeImpl.hpp"
#include "Engine.hpp"
#include "InputPort.hpp"
#include "NodeImpl.hpp"
#include "Notification.hpp"
#include "OutputPort.hpp"
#include "ProcessContext.hpp"
#include "ThreadManager.hpp"
#include "ingen/shared/URIs.hpp"
#include "mix.hpp"
#include "util.hpp"

using namespace std;

namespace Ingen {
namespace Server {

InputPort::InputPort(BufferFactory&      bufs,
                     NodeImpl*           parent,
                     const Raul::Symbol& symbol,
                     uint32_t            index,
                     uint32_t            poly,
                     PortType            type,
                     LV2_URID            buffer_type,
                     const Raul::Atom&   value,
                     size_t              buffer_size)
	: PortImpl(bufs, parent, symbol, index, poly, type, buffer_type, value, buffer_size)
	, _num_edges(0)
{
	const Ingen::Shared::URIs& uris = bufs.uris();

	if (!dynamic_cast<Patch*>(parent))
		add_property(uris.rdf_type, uris.lv2_InputPort);

	// Set default control range
	if (type == PortType::CONTROL || type == PortType::CV) {
		set_property(uris.lv2_minimum, bufs.forge().make(0.0f));
		set_property(uris.lv2_maximum, bufs.forge().make(1.0f));
	}
}

bool
InputPort::apply_poly(ProcessContext& context, Raul::Maid& maid, uint32_t poly)
{
	bool ret = PortImpl::apply_poly(context, maid, poly);
	if (!ret)
		poly = 1;

	assert(_buffers->size() >= poly);

	return true;
}

/** Set @a buffers to the buffers to be used for this port.
 * @return true iff buffers are locally owned by the port
 */
bool
InputPort::get_buffers(Context&                context,
                       BufferFactory&          bufs,
                       Raul::Array<BufferRef>* buffers,
                       uint32_t                poly) const
{
	const bool is_process_context = bufs.engine().is_process_context(context);
	size_t num_edges = is_process_context ? _edges.size() : _num_edges;

	if (is_a(PortType::AUDIO) && num_edges == 0) {
		// Audio input with no edges, use shared zero buffer
		for (uint32_t v = 0; v < poly; ++v)
			buffers->at(v) = bufs.silent_buffer();
		return false;

	} else if (num_edges == 1) {
		if (is_process_context) {
			if (!_edges.front().must_mix() &&
			    !_edges.front().must_queue()) {
				// Single non-mixing connection, use buffers directly
				for (uint32_t v = 0; v < poly; ++v)
					buffers->at(v) = _edges.front().buffer(v);
				return false;
			}
		}
	}

	// Otherwise, allocate local buffers
	for (uint32_t v = 0; v < poly; ++v) {
		buffers->at(v) = _bufs.get(context, buffer_type(), _buffer_size);
		buffers->at(v)->clear();
	}
	return true;
}

/** Add a edge.  Realtime safe.
 *
 * The buffer of this port will be set directly to the edge's buffer
 * if there is only one edge, since no copying/mixing needs to take place.
 *
 * Note that setup_buffers must be called after this before the change
 * will audibly take effect.
 */
void
InputPort::add_edge(ProcessContext& context, EdgeImpl* c)
{
	_edges.push_front(*c);
	_broadcast = true;	// Broadcast value/activity of connected input
}

/** Remove a edge.  Realtime safe.
 *
 * Note that setup_buffers must be called after this before the change
 * will audibly take effect.
 */
EdgeImpl*
InputPort::remove_edge(ProcessContext& context, const OutputPort* tail)
{
	EdgeImpl* edge = NULL;
	for (Edges::iterator i = _edges.begin(); i != _edges.end(); ++i) {
		if (i->tail() == tail) {
			edge = &*i;
			_edges.erase(i);
			break;
		}
	}

	if (!edge) {
		Raul::error << "[InputPort::remove_edge] Edge not found!"
		            << std::endl;
		return NULL;
	}

	// Turn off broadcasting if we're no longer connected
	if (_edges.empty()) {
		if (is_a(PortType::AUDIO)) {
			// Send an update peak of 0.0 to reset to silence
			const Notification note = Notification::make(
				Notification::PORT_ACTIVITY, context.start(), this,
				context.engine().world()->forge().make(0.0f));
			context.event_sink().write(sizeof(note), &note);
		}
		_broadcast = false;
	}

	return edge;
}

/** Prepare buffer for access, mixing if necessary.  Realtime safe.
 */
void
InputPort::pre_process(Context& context)
{
	// If value has been set (e.g. events pushed) by the user, don't smash it
	if (_set_by_user)
		return;

	if (_edges.empty()) {
		for (uint32_t v = 0; v < _poly; ++v) {
			buffer(v)->prepare_read(context);
		}
	} else if (direct_connect()) {
		for (uint32_t v = 0; v < _poly; ++v) {
			_buffers->at(v) = _edges.front().buffer(v);
			_buffers->at(v)->prepare_read(context);
		}
	} else {
		uint32_t max_num_srcs = 1;
		for (Edges::const_iterator e = _edges.begin(); e != _edges.end(); ++e) {
			max_num_srcs += e->tail()->poly();
		}

		Buffer* srcs[max_num_srcs];
		for (uint32_t v = 0; v < _poly; ++v) {
			uint32_t num_srcs = 0;
			for (Edges::iterator e = _edges.begin(); e != _edges.end(); ++e) {
				e->get_sources(context, v, srcs, max_num_srcs, num_srcs);
			}

			mix(context, bufs().uris(), buffer(v).get(), srcs, num_srcs);
			buffer(v)->prepare_read(context);
		}
	}

	if (_broadcast)
		broadcast_value(context, false);
}

void
InputPort::post_process(Context& context)
{
	if (_set_by_user) {
		if (_buffer_type == _bufs.uris().atom_Sequence) {
			// Clear events received via a SetPortValue
			for (uint32_t v = 0; v < _poly; ++v) {
				buffer(v)->clear();
			}
		}
		_set_by_user = false;
	}
}

bool
InputPort::direct_connect() const
{
	return _edges.size() == 1
		&& !_edges.front().must_mix()
		&& !_edges.front().must_queue();
}

} // namespace Server
} // namespace Ingen

<|endoftext|>


<commit_before><commit_msg>[App] [skip ci] - Fix python new references leaking<commit_after><|endoftext|>


<commit_before>/*
  This file is part of Ingen.
  Copyright 2007-2015 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen 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 Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <string>

#include "ingen/Log.hpp"
#include "ingen/URIs.hpp"
#include "lv2/lv2plug.in/ns/ext/presets/presets.h"

#include "Driver.hpp"
#include "Engine.hpp"
#include "LV2Block.hpp"
#include "LV2Plugin.hpp"

using namespace std;

namespace Ingen {
namespace Server {

LV2Plugin::LV2Plugin(World* world, const LilvPlugin* lplugin)
	: PluginImpl(world->uris(),
	             world->uris().lv2_Plugin.urid,
	             Raul::URI(lilv_node_as_uri(lilv_plugin_get_uri(lplugin))))
	, _world(world)
	, _lilv_plugin(lplugin)
{
	set_property(_uris.rdf_type, _uris.lv2_Plugin);

	update_properties();
}

void
LV2Plugin::update_properties()
{
	LilvNode* minor = lilv_world_get(_world->lilv_world(),
	                                 lilv_plugin_get_uri(_lilv_plugin),
	                                 _uris.lv2_minorVersion,
	                                 NULL);
	LilvNode* micro = lilv_world_get(_world->lilv_world(),
	                                 lilv_plugin_get_uri(_lilv_plugin),
	                                 _uris.lv2_minorVersion,
	                                 NULL);

	if (lilv_node_is_int(minor) && lilv_node_is_int(micro)) {
		set_property(_uris.lv2_minorVersion,
		             _world->forge().make(lilv_node_as_int(minor)));
		set_property(_uris.lv2_microVersion,
		             _world->forge().make(lilv_node_as_int(micro)));
	}

	lilv_node_free(minor);
	lilv_node_free(micro);
}

const Raul::Symbol
LV2Plugin::symbol() const
{
	string working = uri();
	if (working.back() == '/')
		working = working.substr(0, working.length() - 1);

	while (working.length() > 0) {
		size_t last_slash = working.find_last_of("/");
		const string symbol = working.substr(last_slash+1);
		if ( (symbol[0] >= 'a' && symbol[0] <= 'z')
		     || (symbol[0] >= 'A' && symbol[0] <= 'Z') )
			return Raul::Symbol::symbolify(symbol);
		else
			working = working.substr(0, last_slash);
	}

	return Raul::Symbol("lv2_symbol");
}

BlockImpl*
LV2Plugin::instantiate(BufferFactory&      bufs,
                       const Raul::Symbol& symbol,
                       bool                polyphonic,
                       GraphImpl*          parent,
                       Engine&             engine)
{
	LV2Block* b = new LV2Block(
		this, symbol, polyphonic, parent, engine.driver()->sample_rate());

	if (!b->instantiate(bufs)) {
		delete b;
		return NULL;
	} else {
		return b;
	}
}

void
LV2Plugin::load_presets()
{
	const URIs& uris    = _world->uris();
	LilvWorld*  lworld  = _world->lilv_world();
	LilvNodes*  presets = lilv_plugin_get_related(_lilv_plugin, uris.pset_Preset);

	if (presets) {
		LILV_FOREACH(nodes, i, presets) {
			const LilvNode* preset = lilv_nodes_get(presets, i);
			lilv_world_load_resource(lworld, preset);

			LilvNodes* labels = lilv_world_find_nodes(
				lworld, preset, uris.rdfs_label, NULL);
			if (labels) {
				const LilvNode* label = lilv_nodes_get_first(labels);

				_presets.insert(
					std::make_pair(Raul::URI(lilv_node_as_uri(preset)),
					               lilv_node_as_string(label)));

				lilv_nodes_free(labels);
			} else {
				_world->log().error(
					fmt("Preset <%1%> has no rdfs:label\n")
					% lilv_node_as_string(lilv_nodes_get(presets, i)));
			}
		}

		lilv_nodes_free(presets);
	}

	PluginImpl::load_presets();
}

} // namespace Server
} // namespace Ingen
<commit_msg>Fix plugin versioning<commit_after>/*
  This file is part of Ingen.
  Copyright 2007-2015 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen 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 Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <string>

#include "ingen/Log.hpp"
#include "ingen/URIs.hpp"
#include "lv2/lv2plug.in/ns/ext/presets/presets.h"

#include "Driver.hpp"
#include "Engine.hpp"
#include "LV2Block.hpp"
#include "LV2Plugin.hpp"

using namespace std;

namespace Ingen {
namespace Server {

LV2Plugin::LV2Plugin(World* world, const LilvPlugin* lplugin)
	: PluginImpl(world->uris(),
	             world->uris().lv2_Plugin.urid,
	             Raul::URI(lilv_node_as_uri(lilv_plugin_get_uri(lplugin))))
	, _world(world)
	, _lilv_plugin(lplugin)
{
	set_property(_uris.rdf_type, _uris.lv2_Plugin);

	update_properties();
}

void
LV2Plugin::update_properties()
{
	LilvNode* minor = lilv_world_get(_world->lilv_world(),
	                                 lilv_plugin_get_uri(_lilv_plugin),
	                                 _uris.lv2_minorVersion,
	                                 NULL);
	LilvNode* micro = lilv_world_get(_world->lilv_world(),
	                                 lilv_plugin_get_uri(_lilv_plugin),
	                                 _uris.lv2_microVersion,
	                                 NULL);

	if (lilv_node_is_int(minor) && lilv_node_is_int(micro)) {
		set_property(_uris.lv2_minorVersion,
		             _world->forge().make(lilv_node_as_int(minor)));
		set_property(_uris.lv2_microVersion,
		             _world->forge().make(lilv_node_as_int(micro)));
	}

	lilv_node_free(minor);
	lilv_node_free(micro);
}

const Raul::Symbol
LV2Plugin::symbol() const
{
	string working = uri();
	if (working.back() == '/')
		working = working.substr(0, working.length() - 1);

	while (working.length() > 0) {
		size_t last_slash = working.find_last_of("/");
		const string symbol = working.substr(last_slash+1);
		if ( (symbol[0] >= 'a' && symbol[0] <= 'z')
		     || (symbol[0] >= 'A' && symbol[0] <= 'Z') )
			return Raul::Symbol::symbolify(symbol);
		else
			working = working.substr(0, last_slash);
	}

	return Raul::Symbol("lv2_symbol");
}

BlockImpl*
LV2Plugin::instantiate(BufferFactory&      bufs,
                       const Raul::Symbol& symbol,
                       bool                polyphonic,
                       GraphImpl*          parent,
                       Engine&             engine)
{
	LV2Block* b = new LV2Block(
		this, symbol, polyphonic, parent, engine.driver()->sample_rate());

	if (!b->instantiate(bufs)) {
		delete b;
		return NULL;
	} else {
		return b;
	}
}

void
LV2Plugin::load_presets()
{
	const URIs& uris    = _world->uris();
	LilvWorld*  lworld  = _world->lilv_world();
	LilvNodes*  presets = lilv_plugin_get_related(_lilv_plugin, uris.pset_Preset);

	if (presets) {
		LILV_FOREACH(nodes, i, presets) {
			const LilvNode* preset = lilv_nodes_get(presets, i);
			lilv_world_load_resource(lworld, preset);

			LilvNodes* labels = lilv_world_find_nodes(
				lworld, preset, uris.rdfs_label, NULL);
			if (labels) {
				const LilvNode* label = lilv_nodes_get_first(labels);

				_presets.insert(
					std::make_pair(Raul::URI(lilv_node_as_uri(preset)),
					               lilv_node_as_string(label)));

				lilv_nodes_free(labels);
			} else {
				_world->log().error(
					fmt("Preset <%1%> has no rdfs:label\n")
					% lilv_node_as_string(lilv_nodes_get(presets, i)));
			}
		}

		lilv_nodes_free(presets);
	}

	PluginImpl::load_presets();
}

} // namespace Server
} // namespace Ingen
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Manasij Mukherjee  <manasij7479@gmail.com>
// author:  Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"

using namespace clang;

namespace cling {

  void AutoloadCallback::report(clang::SourceLocation l,std::string name,std::string header) {
    Sema& sema= m_Interpreter->getSema();

    unsigned id
      = sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
                                                 "Note: '%0' can be found in %1");
/*    unsigned idn //TODO: To be enabled after we have a way to get the full path
      = sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
                                                "Type : %0 , Full Path: %1")*/;

    sema.Diags.Report(l, id) << name << header;

  }

  bool AutoloadCallback::LookupObject (TagDecl *t) {
    if (t->hasAttr<AnnotateAttr>())
      report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
    return false;
  }

  class DefaultArgVisitor: public RecursiveASTVisitor<DefaultArgVisitor> {
  private:
    bool m_IsStoringState;
    AutoloadCallback::FwdDeclsMap* m_Map;
    clang::Preprocessor* m_PP;
  private:
    void InsertIntoAutoloadingState (Decl* decl, std::string annotation) {

      assert(annotation != "" && "Empty annotation!");
      assert(m_PP);

      const FileEntry* FE = 0;
      SourceLocation fileNameLoc;
      bool isAngled = false;
      const DirectoryLookup* LookupFrom = 0;
      const DirectoryLookup* CurDir = 0;

      FE = m_PP->LookupFile(fileNameLoc, annotation, isAngled, LookupFrom,
                            CurDir, /*SearchPath*/0, /*RelativePath*/ 0,
                            /*suggestedModule*/0, /*SkipCache*/false,
                            /*OpenFile*/ false, /*CacheFail*/ false);

      assert(FE && "Must have a valid FileEntry");

      if (m_Map->find(FE) == m_Map->end())
        (*m_Map)[FE] = std::vector<Decl*>();

      (*m_Map)[FE].push_back(decl);
    }

  public:
    DefaultArgVisitor() : m_IsStoringState(false), m_Map(0) {}
    void RemoveDefaultArgsOf(Decl* D) {
      //D = D->getMostRecentDecl();
      TraverseDecl(D);
      //while ((D = D->getPreviousDecl()))
      //  TraverseDecl(D);
    }

    void TrackDefaultArgStateOf(Decl* D, AutoloadCallback::FwdDeclsMap& map,
                                Preprocessor& PP) {
      m_IsStoringState = true;
      m_Map = ↦
      m_PP = &PP;
      TraverseDecl(D);
      m_PP = 0;
      m_Map = 0;
      m_IsStoringState = false;
    }

    bool shouldVisitTemplateInstantiations() { return true; }
    bool TraverseTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitDecl(Decl* D) {
      if (m_IsStoringState)
        return true;

      if (!D->hasAttr<AnnotateAttr>())
        return false;

      AnnotateAttr* attr = D->getAttr<AnnotateAttr>();
      if (!attr)
        return true;

      switch (D->getKind()) {
      default:
        InsertIntoAutoloadingState(D, attr->getAnnotation());
        break;
      case Decl::Enum:
        // EnumDecls have extra information 2 chars after the filename used
        // for extra fixups.
        InsertIntoAutoloadingState(D, attr->getAnnotation().drop_back(2));
        break;
      }

      return true;
    }

    bool TraverseTemplateDecl(TemplateDecl* D) {
      if (!D->getTemplatedDecl()->hasAttr<AnnotateAttr>())
        return false;
      for(auto P: D->getTemplateParameters()->asArray())
        TraverseDecl(P);
      return true;
    }

    bool TraverseNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool TraverseParmVarDecl(ParmVarDecl* D) {
      if (D->hasDefaultArg())
        D->setDefaultArg(nullptr);
      return true;
    }
  };

  void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
                          const clang::Token &IncludeTok,
                          llvm::StringRef FileName,
                          bool IsAngled,
                          clang::CharSourceRange FilenameRange,
                          const clang::FileEntry *File,
                          llvm::StringRef SearchPath,
                          llvm::StringRef RelativePath,
                          const clang::Module *Imported) {
    assert(File && "Must have a valid File");

    auto found = m_Map.find(File);
    if (found == m_Map.end())
     return; // nothing to do, file not referred in any annotation

    DefaultArgVisitor defaultArgsCleaner;
    for (auto D : found->second) {
      defaultArgsCleaner.RemoveDefaultArgsOf(D);
    }
    // Don't need to keep track of cleaned up decls from file.
    m_Map.erase(found);
  }

  AutoloadCallback::AutoloadCallback(Interpreter* interp) :
    InterpreterCallbacks(interp,true,false,true), m_Interpreter(interp){
//#ifdef _POSIX_C_SOURCE
//      //Workaround for differnt expansion of macros to typedefs
//      m_Interpreter->parse("#include <sys/types.h>");
//#endif
  }

  AutoloadCallback::~AutoloadCallback() {
  }

  void AutoloadCallback::TransactionCommitted(const Transaction &T) {
    if (T.decls_begin() == T.decls_end())
      return;
    if (T.decls_begin()->m_DGR.isNull())
      return;

    if (const NamedDecl* ND = dyn_cast<NamedDecl>(*T.decls_begin()->m_DGR.begin()))
      if (ND->getIdentifier() && ND->getName().equals("__Cling_Autoloading_Map")) {
        DefaultArgVisitor defaultArgsStateCollector;
        Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
        for (Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
             I != E; ++I) {
          Transaction::DelayCallInfo DCI = *I;

          // if (DCI.m_Call != Transaction::kCCIHandleTopLevelDecl)
          //   continue;
          if (DCI.m_DGR.isNull())
            continue;

          for (DeclGroupRef::iterator J = DCI.m_DGR.begin(),
                 JE = DCI.m_DGR.end(); J != JE; ++J) {
            defaultArgsStateCollector.TrackDefaultArgStateOf(*J, m_Map, PP);
          }
        }
      }
  }

} //end namespace cling
<commit_msg>Use the correct condition.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Manasij Mukherjee  <manasij7479@gmail.com>
// author:  Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"

using namespace clang;

namespace cling {

  void AutoloadCallback::report(clang::SourceLocation l,std::string name,std::string header) {
    Sema& sema= m_Interpreter->getSema();

    unsigned id
      = sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
                                                 "Note: '%0' can be found in %1");
/*    unsigned idn //TODO: To be enabled after we have a way to get the full path
      = sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
                                                "Type : %0 , Full Path: %1")*/;

    sema.Diags.Report(l, id) << name << header;

  }

  bool AutoloadCallback::LookupObject (TagDecl *t) {
    if (t->hasAttr<AnnotateAttr>())
      report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
    return false;
  }

  class DefaultArgVisitor: public RecursiveASTVisitor<DefaultArgVisitor> {
  private:
    bool m_IsStoringState;
    AutoloadCallback::FwdDeclsMap* m_Map;
    clang::Preprocessor* m_PP;
  private:
    void InsertIntoAutoloadingState (Decl* decl, std::string annotation) {

      assert(annotation != "" && "Empty annotation!");
      assert(m_PP);

      const FileEntry* FE = 0;
      SourceLocation fileNameLoc;
      bool isAngled = false;
      const DirectoryLookup* LookupFrom = 0;
      const DirectoryLookup* CurDir = 0;

      FE = m_PP->LookupFile(fileNameLoc, annotation, isAngled, LookupFrom,
                            CurDir, /*SearchPath*/0, /*RelativePath*/ 0,
                            /*suggestedModule*/0, /*SkipCache*/false,
                            /*OpenFile*/ false, /*CacheFail*/ false);

      assert(FE && "Must have a valid FileEntry");

      if (m_Map->find(FE) == m_Map->end())
        (*m_Map)[FE] = std::vector<Decl*>();

      (*m_Map)[FE].push_back(decl);
    }

  public:
    DefaultArgVisitor() : m_IsStoringState(false), m_Map(0) {}
    void RemoveDefaultArgsOf(Decl* D) {
      //D = D->getMostRecentDecl();
      TraverseDecl(D);
      //while ((D = D->getPreviousDecl()))
      //  TraverseDecl(D);
    }

    void TrackDefaultArgStateOf(Decl* D, AutoloadCallback::FwdDeclsMap& map,
                                Preprocessor& PP) {
      m_IsStoringState = true;
      m_Map = ↦
      m_PP = &PP;
      TraverseDecl(D);
      m_PP = 0;
      m_Map = 0;
      m_IsStoringState = false;
    }

    bool shouldVisitTemplateInstantiations() { return true; }
    bool TraverseTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitDecl(Decl* D) {
      if (!m_IsStoringState)
        return true;

      if (!D->hasAttr<AnnotateAttr>())
        return false;

      AnnotateAttr* attr = D->getAttr<AnnotateAttr>();
      if (!attr)
        return true;

      switch (D->getKind()) {
      default:
        InsertIntoAutoloadingState(D, attr->getAnnotation());
        break;
      case Decl::Enum:
        // EnumDecls have extra information 2 chars after the filename used
        // for extra fixups.
        InsertIntoAutoloadingState(D, attr->getAnnotation().drop_back(2));
        break;
      }

      return true;
    }

    bool TraverseTemplateDecl(TemplateDecl* D) {
      if (!D->getTemplatedDecl()->hasAttr<AnnotateAttr>())
        return false;
      for(auto P: D->getTemplateParameters()->asArray())
        TraverseDecl(P);
      return true;
    }

    bool TraverseNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool TraverseParmVarDecl(ParmVarDecl* D) {
      if (D->hasDefaultArg())
        D->setDefaultArg(nullptr);
      return true;
    }
  };

  void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
                          const clang::Token &IncludeTok,
                          llvm::StringRef FileName,
                          bool IsAngled,
                          clang::CharSourceRange FilenameRange,
                          const clang::FileEntry *File,
                          llvm::StringRef SearchPath,
                          llvm::StringRef RelativePath,
                          const clang::Module *Imported) {
    assert(File && "Must have a valid File");

    auto found = m_Map.find(File);
    if (found == m_Map.end())
     return; // nothing to do, file not referred in any annotation

    DefaultArgVisitor defaultArgsCleaner;
    for (auto D : found->second) {
      defaultArgsCleaner.RemoveDefaultArgsOf(D);
    }
    // Don't need to keep track of cleaned up decls from file.
    m_Map.erase(found);
  }

  AutoloadCallback::AutoloadCallback(Interpreter* interp) :
    InterpreterCallbacks(interp,true,false,true), m_Interpreter(interp){
//#ifdef _POSIX_C_SOURCE
//      //Workaround for differnt expansion of macros to typedefs
//      m_Interpreter->parse("#include <sys/types.h>");
//#endif
  }

  AutoloadCallback::~AutoloadCallback() {
  }

  void AutoloadCallback::TransactionCommitted(const Transaction &T) {
    if (T.decls_begin() == T.decls_end())
      return;
    if (T.decls_begin()->m_DGR.isNull())
      return;

    if (const NamedDecl* ND = dyn_cast<NamedDecl>(*T.decls_begin()->m_DGR.begin()))
      if (ND->getIdentifier() && ND->getName().equals("__Cling_Autoloading_Map")) {
        DefaultArgVisitor defaultArgsStateCollector;
        Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
        for (Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
             I != E; ++I) {
          Transaction::DelayCallInfo DCI = *I;

          // if (DCI.m_Call != Transaction::kCCIHandleTopLevelDecl)
          //   continue;
          if (DCI.m_DGR.isNull())
            continue;

          for (DeclGroupRef::iterator J = DCI.m_DGR.begin(),
                 JE = DCI.m_DGR.end(); J != JE; ++J) {
            defaultArgsStateCollector.TrackDefaultArgStateOf(*J, m_Map, PP);
          }
        }
      }
  }

} //end namespace cling
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Manasij Mukherjee  <manasij7479@gmail.com>
// author:  Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h" // for operator new[](unsigned long, ASTCtx..)
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"

namespace {
  static const char annoTag[] = "$clingAutoload$";
  static const size_t lenAnnoTag = sizeof(annoTag) - 1;
}

using namespace clang;

namespace cling {

  void AutoloadCallback::report(clang::SourceLocation l, llvm::StringRef name,
                                llvm::StringRef header) {
    Sema& sema= m_Interpreter->getSema();

    unsigned id
      = sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
                                                 "Note: '%0' can be found in %1");
/*    unsigned idn //TODO: To be enabled after we have a way to get the full path
      = sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
                                                "Type : %0 , Full Path: %1")*/;

    if (header.startswith(llvm::StringRef(annoTag, lenAnnoTag)))
      sema.Diags.Report(l, id) << name << header.drop_front(lenAnnoTag);

  }

  bool AutoloadCallback::LookupObject (TagDecl *t) {
    if (m_ShowSuggestions && t->hasAttr<AnnotateAttr>())
      report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
    return false;
  }

  class AutoloadingVisitor: public RecursiveASTVisitor<AutoloadingVisitor> {
  private:
    ///\brief Flag determining the visitor's actions. If tru, register autoload
    /// entries, i.e. remember the connection between filename to declaration
    /// that needs to be updated on #include of the filename.
    /// If false, react on an #include by adjusting the forward decls, e.g. by
    /// removing the default tremplate arguments (that will now be provided by
    /// the definition read from the include) and by removing enum declarations
    /// that would otherwise be duplicates.
    bool m_IsStoringState;
    AutoloadCallback::FwdDeclsMap* m_Map;
    clang::Preprocessor* m_PP;
    const clang::FileEntry* m_PrevFE;
    std::string m_PrevFileName;
  private:
    void InsertIntoAutoloadingState (Decl* decl, llvm::StringRef annotation) {

      assert(!annotation.empty() && "Empty annotation!");
      if (!annotation.startswith(llvm::StringRef(annoTag, lenAnnoTag))) {
        // not an autoload annotation.
        return;
      }

      assert(m_PP);

      const FileEntry* FE = 0;
      SourceLocation fileNameLoc;
      bool isAngled = false;
      const DirectoryLookup* LookupFrom = 0;
      const DirectoryLookup* CurDir = 0;
      llvm::StringRef FileName = annotation.drop_front(lenAnnoTag);
      if (FileName.equals(m_PrevFileName))
        FE = m_PrevFE;
      else {
        FE = m_PP->LookupFile(fileNameLoc, FileName, isAngled,
                              LookupFrom, CurDir, /*SearchPath*/0,
                              /*RelativePath*/ 0, /*suggestedModule*/0,
                              /*SkipCache*/ false, /*OpenFile*/ false,
                              /*CacheFail*/ true);
        m_PrevFE = FE;
        m_PrevFileName = FileName;
      }

      assert(FE && "Must have a valid FileEntry");
      if (FE) {
        auto& Vec = (*m_Map)[FE];
        Vec.push_back(decl);
      }
    }

  public:
    AutoloadingVisitor():
      m_IsStoringState(false), m_Map(0), m_PP(0), m_PrevFE(0) {}
    void RemoveDefaultArgsOf(Decl* D) {
      //D = D->getMostRecentDecl();
      TraverseDecl(D);
      //while ((D = D->getPreviousDecl()))
      //  TraverseDecl(D);
    }

    void TrackDefaultArgStateOf(Decl* D, AutoloadCallback::FwdDeclsMap& map,
                                Preprocessor& PP) {
      m_IsStoringState = true;
      m_Map = ↦
      m_PP = &PP;
      TraverseDecl(D);
      m_PP = 0;
      m_Map = 0;
      m_IsStoringState = false;
    }

    bool shouldVisitTemplateInstantiations() { return true; }

    bool VisitDecl(Decl* D) {
      if (!m_IsStoringState)
        return true;

      if (!D->hasAttr<AnnotateAttr>())
        return true;

      AnnotateAttr* attr = D->getAttr<AnnotateAttr>();
      if (!attr)
        return true;

      switch (D->getKind()) {
      default:
        InsertIntoAutoloadingState(D, attr->getAnnotation());
        break;
      case Decl::Enum:
        // EnumDecls have extra information 2 chars after the filename used
        // for extra fixups.
          EnumDecl* ED = cast<EnumDecl>(D);
          if (ED->isFixed()) {
            StringRef str = ED->getAttr<AnnotateAttr>()->getAnnotation();
            char ch = str.back();
//            str.drop_back(2);
            ED->getAttr<AnnotateAttr>()->setAnnotation(ED->getASTContext(), str);
            struct EnumDeclDerived: public EnumDecl {
              static void setFixed(EnumDecl* ED, bool value = true) {
                ((EnumDeclDerived*)ED)->IsFixed = value;
              }
            };

            if (ch != '1')
              EnumDeclDerived::setFixed(ED, false);
          }
        InsertIntoAutoloadingState(D, attr->getAnnotation().drop_back(2));
        break;
      }

      return true;
    }

    bool VisitCXXRecordDecl(CXXRecordDecl* D) {
      if (!D->hasAttr<AnnotateAttr>())
        return true;

      VisitDecl(D);

      if (ClassTemplateDecl* TmplD = D->getDescribedClassTemplate())
        return VisitTemplateDecl(TmplD);
      return true;
    }

    bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
      VisitDecl(D);

      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitTemplateDecl(TemplateDecl* D) {
      if (D->getTemplatedDecl() &&
          !D->getTemplatedDecl()->hasAttr<AnnotateAttr>())
        return true;

      VisitDecl(D);

      for(auto P: D->getTemplateParameters()->asArray())
        TraverseDecl(P);
      return true;
    }

    bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl* D) {
      VisitDecl(D);

      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
      VisitDecl(D);

      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitParmVarDecl(ParmVarDecl* D) {
      VisitDecl(D);

      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArg())
        D->setDefaultArg(nullptr);
      return true;
    }
  };

  void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
                          const clang::Token &IncludeTok,
                          llvm::StringRef FileName,
                          bool IsAngled,
                          clang::CharSourceRange FilenameRange,
                          const clang::FileEntry *File,
                          llvm::StringRef SearchPath,
                          llvm::StringRef RelativePath,
                          const clang::Module *Imported) {
    // If File is 0 this means that the #included file doesn't exist.
    if (!File)
      return;

    auto found = m_Map.find(File);
    if (found == m_Map.end())
     return; // nothing to do, file not referred in any annotation

    AutoloadingVisitor defaultArgsCleaner;
    for (auto D : found->second) {
      defaultArgsCleaner.RemoveDefaultArgsOf(D);
    }
    // Don't need to keep track of cleaned up decls from file.
    m_Map.erase(found);
  }

  AutoloadCallback::~AutoloadCallback() {
  }

  void AutoloadCallback::TransactionCommitted(const Transaction &T) {
    if (T.decls_begin() == T.decls_end())
      return;
    if (T.decls_begin()->m_DGR.isNull())
      return;

    if (const NamedDecl* ND = dyn_cast<NamedDecl>(*T.decls_begin()->m_DGR.begin()))
      if (ND->getIdentifier() && ND->getName().equals("__Cling_Autoloading_Map")) {
        AutoloadingVisitor defaultArgsStateCollector;
        Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
        for (Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
             I != E; ++I) {
          Transaction::DelayCallInfo DCI = *I;

          // if (DCI.m_Call != Transaction::kCCIHandleTopLevelDecl)
          //   continue;
          if (DCI.m_DGR.isNull())
            continue;

          if (const NamedDecl* ND = dyn_cast<NamedDecl>(*T.decls_begin()->m_DGR.begin()))
            if (ND->getIdentifier()
                && ND->getName().equals("__Cling_Autoloading_Map")) {

              for (Transaction::const_iterator I = T.decls_begin(),
                     E = T.decls_end(); I != E; ++I) {
                Transaction::DelayCallInfo DCI = *I;
                for (DeclGroupRef::iterator J = DCI.m_DGR.begin(),
                       JE = DCI.m_DGR.end(); J != JE; ++J) {
                    defaultArgsStateCollector.TrackDefaultArgStateOf(*J, m_Map, PP);
                }
              }
            }

        }
      }
  }

} //end namespace cling
<commit_msg>Keep default args once there is a template definition (ROOT-7037).<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Manasij Mukherjee  <manasij7479@gmail.com>
// author:  Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h" // for operator new[](unsigned long, ASTCtx..)
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"

namespace {
  static const char annoTag[] = "$clingAutoload$";
  static const size_t lenAnnoTag = sizeof(annoTag) - 1;
}

using namespace clang;

namespace cling {

  void AutoloadCallback::report(clang::SourceLocation l, llvm::StringRef name,
                                llvm::StringRef header) {
    Sema& sema= m_Interpreter->getSema();

    unsigned id
      = sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
                                                 "Note: '%0' can be found in %1");
/*    unsigned idn //TODO: To be enabled after we have a way to get the full path
      = sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
                                                "Type : %0 , Full Path: %1")*/;

    if (header.startswith(llvm::StringRef(annoTag, lenAnnoTag)))
      sema.Diags.Report(l, id) << name << header.drop_front(lenAnnoTag);

  }

  bool AutoloadCallback::LookupObject (TagDecl *t) {
    if (m_ShowSuggestions && t->hasAttr<AnnotateAttr>())
      report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
    return false;
  }

  class AutoloadingVisitor: public RecursiveASTVisitor<AutoloadingVisitor> {
  private:
    ///\brief Flag determining the visitor's actions. If tru, register autoload
    /// entries, i.e. remember the connection between filename to declaration
    /// that needs to be updated on #include of the filename.
    /// If false, react on an #include by adjusting the forward decls, e.g. by
    /// removing the default tremplate arguments (that will now be provided by
    /// the definition read from the include) and by removing enum declarations
    /// that would otherwise be duplicates.
    bool m_IsStoringState;
    AutoloadCallback::FwdDeclsMap* m_Map;
    clang::Preprocessor* m_PP;
    const clang::FileEntry* m_PrevFE;
    std::string m_PrevFileName;
  private:
    void InsertIntoAutoloadingState (Decl* decl, llvm::StringRef annotation) {

      assert(!annotation.empty() && "Empty annotation!");
      if (!annotation.startswith(llvm::StringRef(annoTag, lenAnnoTag))) {
        // not an autoload annotation.
        return;
      }

      assert(m_PP);

      const FileEntry* FE = 0;
      SourceLocation fileNameLoc;
      bool isAngled = false;
      const DirectoryLookup* LookupFrom = 0;
      const DirectoryLookup* CurDir = 0;
      llvm::StringRef FileName = annotation.drop_front(lenAnnoTag);
      if (FileName.equals(m_PrevFileName))
        FE = m_PrevFE;
      else {
        FE = m_PP->LookupFile(fileNameLoc, FileName, isAngled,
                              LookupFrom, CurDir, /*SearchPath*/0,
                              /*RelativePath*/ 0, /*suggestedModule*/0,
                              /*SkipCache*/ false, /*OpenFile*/ false,
                              /*CacheFail*/ true);
        m_PrevFE = FE;
        m_PrevFileName = FileName;
      }

      assert(FE && "Must have a valid FileEntry");
      if (FE) {
        auto& Vec = (*m_Map)[FE];
        Vec.push_back(decl);
      }
    }

  public:
    AutoloadingVisitor():
      m_IsStoringState(false), m_Map(0), m_PP(0), m_PrevFE(0) {}
    void RemoveDefaultArgsOf(Decl* D) {
      //D = D->getMostRecentDecl();
      TraverseDecl(D);
      //while ((D = D->getPreviousDecl()))
      //  TraverseDecl(D);
    }

    void TrackDefaultArgStateOf(Decl* D, AutoloadCallback::FwdDeclsMap& map,
                                Preprocessor& PP) {
      m_IsStoringState = true;
      m_Map = ↦
      m_PP = &PP;
      TraverseDecl(D);
      m_PP = 0;
      m_Map = 0;
      m_IsStoringState = false;
    }

    bool shouldVisitTemplateInstantiations() { return true; }

    bool VisitDecl(Decl* D) {
      if (!m_IsStoringState)
        return true;

      if (!D->hasAttr<AnnotateAttr>())
        return true;

      AnnotateAttr* attr = D->getAttr<AnnotateAttr>();
      if (!attr)
        return true;

      switch (D->getKind()) {
      default:
        InsertIntoAutoloadingState(D, attr->getAnnotation());
        break;
      case Decl::Enum:
        // EnumDecls have extra information 2 chars after the filename used
        // for extra fixups.
          EnumDecl* ED = cast<EnumDecl>(D);
          if (ED->isFixed()) {
            StringRef str = ED->getAttr<AnnotateAttr>()->getAnnotation();
            char ch = str.back();
//            str.drop_back(2);
            ED->getAttr<AnnotateAttr>()->setAnnotation(ED->getASTContext(), str);
            struct EnumDeclDerived: public EnumDecl {
              static void setFixed(EnumDecl* ED, bool value = true) {
                ((EnumDeclDerived*)ED)->IsFixed = value;
              }
            };

            if (ch != '1')
              EnumDeclDerived::setFixed(ED, false);
          }
        InsertIntoAutoloadingState(D, attr->getAnnotation().drop_back(2));
        break;
      }

      return true;
    }

    bool VisitCXXRecordDecl(CXXRecordDecl* D) {
      if (!D->hasAttr<AnnotateAttr>())
        return true;

      VisitDecl(D);

      if (ClassTemplateDecl* TmplD = D->getDescribedClassTemplate())
        return VisitTemplateDecl(TmplD);
      return true;
    }

    bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
      VisitDecl(D);

      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitTemplateDecl(TemplateDecl* D) {
      if (D->getTemplatedDecl() &&
          !D->getTemplatedDecl()->hasAttr<AnnotateAttr>())
        return true;

      VisitDecl(D);

      // If we have a definition we might be about to re-#include the
      // same header containing definition that was #included previously,
      // i.e. we might have multiple fwd decls for the same template.
      // DO NOT remove the defaults here; the definition needs to keep it.
      // (ROOT-7037)
      if (ClassTemplateDecl* CTD = dyn_cast<ClassTemplateDecl>(D))
        if (CXXRecordDecl* TemplatedD = CTD->getTemplatedDecl())
          if (TemplatedD->getDefinition())
            return true;

      for(auto P: D->getTemplateParameters()->asArray())
        TraverseDecl(P);
      return true;
    }

    bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl* D) {
      VisitDecl(D);

      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
      VisitDecl(D);

      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArgument())
        D->removeDefaultArgument();
      return true;
    }

    bool VisitParmVarDecl(ParmVarDecl* D) {
      VisitDecl(D);

      if (m_IsStoringState)
        return true;

      if (D->hasDefaultArg())
        D->setDefaultArg(nullptr);
      return true;
    }
  };

  void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
                          const clang::Token &IncludeTok,
                          llvm::StringRef FileName,
                          bool IsAngled,
                          clang::CharSourceRange FilenameRange,
                          const clang::FileEntry *File,
                          llvm::StringRef SearchPath,
                          llvm::StringRef RelativePath,
                          const clang::Module *Imported) {
    // If File is 0 this means that the #included file doesn't exist.
    if (!File)
      return;

    auto found = m_Map.find(File);
    if (found == m_Map.end())
     return; // nothing to do, file not referred in any annotation

    AutoloadingVisitor defaultArgsCleaner;
    for (auto D : found->second) {
      defaultArgsCleaner.RemoveDefaultArgsOf(D);
    }
    // Don't need to keep track of cleaned up decls from file.
    m_Map.erase(found);
  }

  AutoloadCallback::~AutoloadCallback() {
  }

  void AutoloadCallback::TransactionCommitted(const Transaction &T) {
    if (T.decls_begin() == T.decls_end())
      return;
    if (T.decls_begin()->m_DGR.isNull())
      return;

    if (const NamedDecl* ND = dyn_cast<NamedDecl>(*T.decls_begin()->m_DGR.begin()))
      if (ND->getIdentifier() && ND->getName().equals("__Cling_Autoloading_Map")) {
        AutoloadingVisitor defaultArgsStateCollector;
        Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
        for (Transaction::const_iterator I = T.decls_begin(), E = T.decls_end();
             I != E; ++I) {
          Transaction::DelayCallInfo DCI = *I;

          // if (DCI.m_Call != Transaction::kCCIHandleTopLevelDecl)
          //   continue;
          if (DCI.m_DGR.isNull())
            continue;

          if (const NamedDecl* ND = dyn_cast<NamedDecl>(*T.decls_begin()->m_DGR.begin()))
            if (ND->getIdentifier()
                && ND->getName().equals("__Cling_Autoloading_Map")) {

              for (Transaction::const_iterator I = T.decls_begin(),
                     E = T.decls_end(); I != E; ++I) {
                Transaction::DelayCallInfo DCI = *I;
                for (DeclGroupRef::iterator J = DCI.m_DGR.begin(),
                       JE = DCI.m_DGR.end(); J != JE; ++J) {
                    defaultArgsStateCollector.TrackDefaultArgStateOf(*J, m_Map, PP);
                }
              }
            }

        }
      }
  }

} //end namespace cling
<|endoftext|>