Ejemplo n.º 1
0
KviIpEditor::KviIpEditor(QWidget * parent, AddressType addrType, const QString & ipAddr, const char * name)
    : QLineEdit(parent)
{
	setObjectName(name);
	setAddressType(addrType);
	setAddress(ipAddr);
}
Ejemplo n.º 2
0
KviIpEditor::KviIpEditor(QWidget * parent,AddressType addrType,const QString &ipAddr,const char *name)
    :QFrame(parent)
{
    setObjectName(name);
    for(int i=0; i<7; i++)
    {
        m_pEdit[i]  = 0;
        m_pLabel[i] = 0;
    }
    m_pEdit[7] = 0;
    setFrameStyle(QFrame::Sunken|QFrame::StyledPanel);
    setBackgroundRole(QPalette::Base);
    setAddressType(addrType);
    setAddress(ipAddr);
}
/**
 * Offer an Unix socket over this stream tube.
 *
 * This method offers an Unix socket over this stream tube. The socket address is given as a
 * a QString, which should contain the path to the socket. Abstract Unix sockets are also supported,
 * and are given as addresses prefixed with a \c NUL byte.
 *
 * If your application uses QLocalServer as the local Unix server implementation, you can use the
 * offerUnixSocket(const QLocalServer *, const QVariantMap &, bool) overload instead to more easily
 * pass the server's listen address.
 *
 * Note that only connection managers for which supportsUnixSocketsOnLocalhost() or
 * supportsAbstractUnixSocketsOnLocalhost() is \c true support exporting Unix sockets.
 *
 * If supportsUnixSocketsWithCredentials() or supportsAbstractUnixSocketsWithCredentials(), as
 * appropriate, returns \c true, the \c requireCredentials parameter can be set to \c true to make
 * the connection manager pass an SCM_CREDS or SCM_CREDENTIALS message as supported by the platform
 * when making a new connection. This enables preventing other local users from connecting to the
 * service, but might not be possible to use with all protocols as the message is in-band in the
 * data stream.
 *
 * Arbitrary parameters can be associated with the offer to bootstrap legacy protocols; these will
 * in particular be available as IncomingStreamTubeChannel::parameters() for a tube receiver
 * implemented using TelepathyQt in the other end.
 *
 * This method requires OutgoingStreamTubeChannel::FeatureCore to be ready.
 *
 * \param socketAddress A valid path to an existing Unix socket or abstract Unix socket.
 * \param parameters A dictionary of arbitrary parameters to send with the tube offer.
 * \param requireCredentials Whether the server requires a SCM_CREDS or SCM_CREDENTIALS message
 *                           upon connection.
 * \return A PendingOperation which will emit PendingOperation::finished
 *         when the stream tube is ready to be used
 *         (hence in the #TubeStateOpen state).
 */
PendingOperation *OutgoingStreamTubeChannel::offerUnixSocket(
        const QString &socketAddress,
        const QVariantMap &parameters,
        bool requireCredentials)
{
    SocketAccessControl accessControl = requireCredentials ?
            SocketAccessControlCredentials :
            SocketAccessControlLocalhost;

    if (!isReady(OutgoingStreamTubeChannel::FeatureCore)) {
        warning() << "OutgoingStreamTubeChannel::FeatureCore must be ready before "
                "calling offerTube";
        return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
                QLatin1String("Channel not ready"),
                OutgoingStreamTubeChannelPtr(this));
    }

    // The tube must be not offered
    if (state() != TubeChannelStateNotOffered) {
        warning() << "You can not expose more than a socket for each Stream Tube";
        return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
                QLatin1String("Channel busy"), OutgoingStreamTubeChannelPtr(this));
    }

    // In this specific overload, we're handling an Unix/AbstractUnix socket
    if (socketAddress.startsWith(QLatin1Char('\0'))) {
        // Abstract Unix socket case
        // Check if the combination type/access control is supported
        if ((accessControl == SocketAccessControlLocalhost &&
                !supportsAbstractUnixSocketsOnLocalhost()) ||
            (accessControl == SocketAccessControlCredentials &&
                !supportsAbstractUnixSocketsWithCredentials()) ) {
            warning() << "You requested an address type/access control combination "
                    "not supported by this channel";
            return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
                    QLatin1String("The requested address type/access control "
                            "combination is not supported"),
                    OutgoingStreamTubeChannelPtr(this));
        }

        setAddressType(SocketAddressTypeAbstractUnix);
        setAccessControl(accessControl);
        setLocalAddress(socketAddress);

        PendingVoid *pv = new PendingVoid(
                interface<Client::ChannelTypeStreamTubeInterface>()->Offer(
                        SocketAddressTypeAbstractUnix,
                        QDBusVariant(QVariant(socketAddress.toLatin1())),
                        accessControl,
                        parameters),
                OutgoingStreamTubeChannelPtr(this));
        PendingOpenTube *op = new PendingOpenTube(pv, parameters,
                OutgoingStreamTubeChannelPtr(this));
        return op;
    } else {
        // Unix socket case
        // Check if the combination type/access control is supported
        if ((accessControl == SocketAccessControlLocalhost &&
                !supportsUnixSocketsOnLocalhost()) ||
            (accessControl == SocketAccessControlCredentials &&
                !supportsUnixSocketsWithCredentials()) ||
            (accessControl != SocketAccessControlLocalhost &&
                accessControl != SocketAccessControlCredentials) ) {
            warning() << "You requested an address type/access control combination "
                "not supported by this channel";
            return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
                    QLatin1String("The requested address type/access control "
                            "combination is not supported"),
                    OutgoingStreamTubeChannelPtr(this));
        }

        setAddressType(SocketAddressTypeUnix);
        setAccessControl(accessControl);
        setLocalAddress(socketAddress);

        PendingVoid *pv = new PendingVoid(
                interface<Client::ChannelTypeStreamTubeInterface>()->Offer(
                        SocketAddressTypeUnix,
                        QDBusVariant(QVariant(socketAddress.toLatin1())),
                        accessControl,
                        parameters),
                OutgoingStreamTubeChannelPtr(this));
        PendingOpenTube *op = new PendingOpenTube(pv, parameters,
                OutgoingStreamTubeChannelPtr(this));
        return op;
    }
}
/**
 * Offer a TCP socket over this stream tube.
 *
 * This method offers a TCP socket over this tube. The socket's address is given as
 * a QHostAddress and a numerical port in native byte order.
 *
 * If your application uses QTcpServer as the local TCP server implementation, you can use the
 * offerTcpSocket(const QTcpServer *, const QVariantMap &) overload instead to more easily pass the
 * server's listen address.
 *
 * It is guaranteed that when the PendingOperation returned by this method will be completed,
 * the tube will be opened and ready to be used.
 *
 * Connection managers adhering to the \telepathy_spec should always support offering IPv4 TCP
 * sockets. IPv6 sockets are only supported if supportsIPv6SocketsOnLocalhost() is \c true.
 *
 * Note that the library will try to use #SocketAccessControlPort access control whenever possible,
 * as it allows to map connections to users based on their source addresses. If
 * supportsIPv4SocketsWithSpecifiedAddress() or supportsIPv6SocketsWithSpecifiedAddress() for IPv4
 * and IPv6 sockets respectively is \c false, this feature is not available, and the
 * connectionsForSourceAddresses() map won't contain useful distinct keys.
 *
 * Arbitrary parameters can be associated with the offer to bootstrap legacy protocols; these will
 * in particular be available as IncomingStreamTubeChannel::parameters() for a tube receiver
 * implemented using TelepathyQt in the other end.
 *
 * This method requires OutgoingStreamTubeChannel::FeatureCore to be ready.
 *
 * \param address A valid IPv4 or IPv6 address pointing to an existing socket.
 * \param port The port the socket is listening for connections to.
 * \param parameters A dictionary of arbitrary parameters to send with the tube offer.
 * \return A PendingOperation which will emit PendingOperation::finished
 *         when the stream tube is ready to be used
 *         (hence in the #TubeStateOpen state).
 */
PendingOperation *OutgoingStreamTubeChannel::offerTcpSocket(
        const QHostAddress &address,
        quint16 port,
        const QVariantMap &parameters)
{
    if (!isReady(OutgoingStreamTubeChannel::FeatureCore)) {
        warning() << "OutgoingStreamTubeChannel::FeatureCore must be ready before "
                "calling offerTube";
        return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
                QLatin1String("Channel not ready"),
                OutgoingStreamTubeChannelPtr(this));
    }

    // The tube must be not offered
    if (state() != TubeChannelStateNotOffered) {
        warning() << "You can not expose more than a socket for each Stream Tube";
        return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
                QLatin1String("Channel busy"),
                OutgoingStreamTubeChannelPtr(this));
    }

    SocketAccessControl accessControl = SocketAccessControlLocalhost;
    // Check if port is supported

    QHostAddress hostAddress = address;
#if QT_VERSION >= 0x050000
    if (hostAddress == QHostAddress::Any) {
        hostAddress = QHostAddress::AnyIPv4;
    }
#endif

    // In this specific overload, we're handling an IPv4/IPv6 socket
    if (hostAddress.protocol() == QAbstractSocket::IPv4Protocol) {
        // IPv4 case
        SocketAccessControl accessControl;
        // Do some heuristics to find out the best access control.We always prefer port for tracking
        // connections and source addresses.
        if (supportsIPv4SocketsWithSpecifiedAddress()) {
            accessControl = SocketAccessControlPort;
        } else if (supportsIPv4SocketsOnLocalhost()) {
            accessControl = SocketAccessControlLocalhost;
        } else {
            // There are no combinations supported for this socket
            warning() << "You requested an address type/access control combination "
                    "not supported by this channel";
            return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
                    QLatin1String("The requested address type/access control "
                            "combination is not supported"),
                    OutgoingStreamTubeChannelPtr(this));
        }

        setAddressType(SocketAddressTypeIPv4);
        setAccessControl(accessControl);
        setIpAddress(qMakePair<QHostAddress, quint16>(hostAddress, port));

        SocketAddressIPv4 addr;
        addr.address = hostAddress.toString();
        addr.port = port;

        PendingVoid *pv = new PendingVoid(
                interface<Client::ChannelTypeStreamTubeInterface>()->Offer(
                        SocketAddressTypeIPv4,
                        QDBusVariant(QVariant::fromValue(addr)),
                        accessControl,
                        parameters),
                OutgoingStreamTubeChannelPtr(this));
        PendingOpenTube *op = new PendingOpenTube(pv, parameters,
                OutgoingStreamTubeChannelPtr(this));
        return op;
    } else if (hostAddress.protocol() == QAbstractSocket::IPv6Protocol) {
        // IPv6 case
        // Do some heuristics to find out the best access control.We always prefer port for tracking
        // connections and source addresses.
        if (supportsIPv6SocketsWithSpecifiedAddress()) {
            accessControl = SocketAccessControlPort;
        } else if (supportsIPv6SocketsOnLocalhost()) {
            accessControl = SocketAccessControlLocalhost;
        } else {
            // There are no combinations supported for this socket
            warning() << "You requested an address type/access control combination "
                    "not supported by this channel";
            return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
                    QLatin1String("The requested address type/access control "
                            "combination is not supported"),
                    OutgoingStreamTubeChannelPtr(this));
        }

        setAddressType(SocketAddressTypeIPv6);
        setAccessControl(accessControl);
        setIpAddress(qMakePair<QHostAddress, quint16>(hostAddress, port));

        SocketAddressIPv6 addr;
        addr.address = hostAddress.toString();
        addr.port = port;

        PendingVoid *pv = new PendingVoid(
                interface<Client::ChannelTypeStreamTubeInterface>()->Offer(
                        SocketAddressTypeIPv6,
                        QDBusVariant(QVariant::fromValue(addr)),
                        accessControl,
                        parameters),
                OutgoingStreamTubeChannelPtr(this));
        PendingOpenTube *op = new PendingOpenTube(pv, parameters,
                OutgoingStreamTubeChannelPtr(this));
        return op;
    } else {
        // We're handling an IPv4/IPv6 socket only
        warning() << "offerTube can be called only with a QHostAddress representing "
                "an IPv4 or IPv6 address";
        return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
                QLatin1String("Invalid host given"),
                OutgoingStreamTubeChannelPtr(this));
    }

}
/**
 * Accept an incoming stream tube as a Unix socket.
 *
 * This method accepts an incoming connection request for a stream tube. It can be called
 * only if the tube is in the #TubeStateLocalPending state.
 *
 * An Unix socket (can be used with QLocalSocket or alike) will be opened by the connection manager
 * as the local tube endpoint. This is only supported if supportsUnixSocketsOnLocalhost() is \c
 * true.
 *
 * You can also specify whether the CM should require an SCM_CREDS or SCM_CREDENTIALS message
 * upon connection instead of accepting every incoming connection from localhost. This provides
 * additional security, but requires sending the byte retrieved from
 * PendingStreamTubeConnection::credentialByte() in-line in the socket byte stream (in a credentials
 * message if available on the platform), which might not be compatible with all protocols or
 * libraries. Also, only connection managers for which supportsUnixSocketsWithCredentials() is \c
 * true support this type of access control.
 *
 * This method requires IncomingStreamTubeChannel::FeatureCore to be ready.
 *
 * \param requireCredentials Whether the CM should require an SCM_CREDS or SCM_CREDENTIALS message
 *                           upon connection.
 * \return A PendingStreamTubeConnection which will emit PendingStreamTubeConnection::finished
 *         when the stream tube is ready to be used
 *         (hence in the #TubeStateOpen state).
 * \sa StreamTubeChannel::supportsUnixSocketsOnLocalhost(),
 *     StreamTubeChannel::supportsUnixSocketsWithCredentials(),
 *     StreamTubeChannel::supportsAbstractUnixSocketsOnLocalhost(),
 *     StreamTubeChannel::supportsAbstractUnixSocketsWithCredentials()
 */
PendingStreamTubeConnection *IncomingStreamTubeChannel::acceptTubeAsUnixSocket(
        bool requireCredentials)
{
    if (!isReady(IncomingStreamTubeChannel::FeatureCore)) {
        warning() << "IncomingStreamTubeChannel::FeatureCore must be ready before "
                "calling acceptTubeAsUnixSocket";
        return new PendingStreamTubeConnection(TP_QT_ERROR_NOT_AVAILABLE,
                QLatin1String("Channel not ready"),
                IncomingStreamTubeChannelPtr(this));
    }

    // The tube must be in local pending state
    if (state() != TubeChannelStateLocalPending) {
        warning() << "You can accept tubes only when they are in LocalPending state";
        return new PendingStreamTubeConnection(TP_QT_ERROR_NOT_AVAILABLE,
                QLatin1String("Channel not ready"),
                IncomingStreamTubeChannelPtr(this));
    }

    SocketAccessControl accessControl = requireCredentials ?
            SocketAccessControlCredentials :
            SocketAccessControlLocalhost;
    setAddressType(SocketAddressTypeUnix);
    setAccessControl(accessControl);

    // Fail early if the combination is not supported
    if ((accessControl == SocketAccessControlLocalhost &&
            addressType() == SocketAddressTypeUnix &&
            !supportsUnixSocketsOnLocalhost()) ||
        (accessControl == SocketAccessControlCredentials &&
            addressType() == SocketAddressTypeUnix &&
            !supportsUnixSocketsWithCredentials()) ||
        (accessControl == SocketAccessControlLocalhost &&
            addressType() == SocketAddressTypeAbstractUnix &&
           !supportsAbstractUnixSocketsOnLocalhost()) ||
        (accessControl == SocketAccessControlCredentials &&
           addressType() == SocketAddressTypeAbstractUnix &&
           !supportsAbstractUnixSocketsWithCredentials())) {
        warning() << "You requested an address type/access control combination "
                "not supported by this channel";
        return new PendingStreamTubeConnection(TP_QT_ERROR_NOT_IMPLEMENTED,
                QLatin1String("The requested address type/access control "
                        "combination is not supported"),
                IncomingStreamTubeChannelPtr(this));
    }

    QDBusVariant accessControlParam;
    uchar credentialByte = 0;
    if (accessControl == SocketAccessControlLocalhost) {
        accessControlParam.setVariant(qVariantFromValue(static_cast<uint>(0)));
    } else if (accessControl == SocketAccessControlCredentials) {
        if (mPriv->initRandom) {
            qsrand(QTime::currentTime().msec());
            mPriv->initRandom = false;
        }
        credentialByte = static_cast<uchar>(qrand());
        accessControlParam.setVariant(qVariantFromValue(credentialByte));
    } else {
        Q_ASSERT(false);
    }

    // Perform the actual call
    PendingVariant *pv = new PendingVariant(
            interface<Client::ChannelTypeStreamTubeInterface>()->Accept(
                    addressType(),
                    accessControl,
                    accessControlParam),
            IncomingStreamTubeChannelPtr(this));

    PendingStreamTubeConnection *op = new PendingStreamTubeConnection(pv, addressType(),
            requireCredentials, credentialByte, IncomingStreamTubeChannelPtr(this));
    return op;
}
/**
 * Accept an incoming stream tube as a TCP socket.
 *
 * This method accepts an incoming connection request for a stream tube. It can be called
 * only if the tube is in the #TubeStateLocalPending state.
 *
 * The connection manager will open a TCP socket for the application to connect to. The address of
 * the socket will be returned in PendingStreamTubeConnection::ipAddress() once the operation has
 * finished successfully.
 *
 * This overload lets you specify an allowed address/port combination for connecting to the CM
 * socket. Connections with other source addresses won't be accepted. The accessors
 * supportsIPv4SocketsWithSpecifiedAddress() and supportsIPv6SocketsWithSpecifiedAddress() can be
 * used to verify that the connection manager supports this kind of access control; otherwise, this
 * method will always fail unless QHostAddress::Any (or QHostAddress::AnyIPv4 in Qt5) or
 * QHostAddress::AnyIPv6 is passed, in which case the behavior is identical to the always supported
 * acceptTubeAsTcpSocket() overload.
 *
 * Note that when using QHostAddress::Any (or QHostAddress::AnyIPv4 in Qt5) or
 * QHostAddress::AnyIPv6, \a allowedPort is ignored.
 *
 * This method requires IncomingStreamTubeChannel::FeatureCore to be ready.
 *
 * \param allowedAddress An allowed address for connecting to the socket.
 * \param allowedPort An allowed port for connecting to the socket.
 * \return A PendingStreamTubeConnection which will emit PendingStreamTubeConnection::finished
 *         when the stream tube is ready to be used
 *         (hence in the #TubeStateOpen state).
 */
PendingStreamTubeConnection *IncomingStreamTubeChannel::acceptTubeAsTcpSocket(
        const QHostAddress &allowedAddress,
        quint16 allowedPort)
{
    if (!isReady(IncomingStreamTubeChannel::FeatureCore)) {
        warning() << "IncomingStreamTubeChannel::FeatureCore must be ready before "
                "calling acceptTubeAsTcpSocket";
        return new PendingStreamTubeConnection(TP_QT_ERROR_NOT_AVAILABLE,
                QLatin1String("Channel not ready"),
                IncomingStreamTubeChannelPtr(this));
    }

    // The tube must be in local pending state
    if (state() != TubeChannelStateLocalPending) {
        warning() << "You can accept tubes only when they are in LocalPending state";
        return new PendingStreamTubeConnection(TP_QT_ERROR_NOT_AVAILABLE,
                QLatin1String("Channel not ready"),
                IncomingStreamTubeChannelPtr(this));
    }

    QVariant controlParameter;
    SocketAccessControl accessControl;
    QHostAddress hostAddress = allowedAddress;

#if QT_VERSION >= 0x050000
    if (hostAddress == QHostAddress::Any) {
        hostAddress = QHostAddress::AnyIPv4;
    }
#endif

    // Now, let's check what we need to do with accessControl. There is just one special case, Port.
    if (hostAddress != QHostAddress::Any &&
#if QT_VERSION >= 0x050000
        hostAddress != QHostAddress::AnyIPv4 &&
#endif
        hostAddress != QHostAddress::AnyIPv6) {
        // We need to have a valid QHostAddress AND Port.
        if (hostAddress.isNull() || allowedPort == 0) {
            warning() << "You have to set a valid allowed address+port to use Port access control";
            return new PendingStreamTubeConnection(TP_QT_ERROR_INVALID_ARGUMENT,
                    QLatin1String("The supplied allowed address and/or port was invalid"),
                    IncomingStreamTubeChannelPtr(this));
        }

        accessControl = SocketAccessControlPort;

        // IPv4 or IPv6?
        if (hostAddress.protocol() == QAbstractSocket::IPv4Protocol) {
            // IPv4 case
            SocketAddressIPv4 addr;
            addr.address = hostAddress.toString();
            addr.port = allowedPort;

            controlParameter = QVariant::fromValue(addr);
        } else if (hostAddress.protocol() == QAbstractSocket::IPv6Protocol) {
            // IPv6 case
            SocketAddressIPv6 addr;
            addr.address = hostAddress.toString();
            addr.port = allowedPort;

            controlParameter = QVariant::fromValue(addr);
        } else {
            // We're handling an IPv4/IPv6 socket only
            warning() << "acceptTubeAsTcpSocket can be called only with a QHostAddress "
                    "representing an IPv4 or IPv6 address";
            return new PendingStreamTubeConnection(TP_QT_ERROR_INVALID_ARGUMENT,
                    QLatin1String("Invalid host given"),
                    IncomingStreamTubeChannelPtr(this));
        }
    } else {
        // We have to do no special stuff here
        accessControl = SocketAccessControlLocalhost;
        // Since QDBusMarshaller does not like null variants, just add an empty string.
        controlParameter = QVariant(QString());
    }

    // Set the correct address type and access control
    setAddressType(hostAddress.protocol() == QAbstractSocket::IPv4Protocol ?
            SocketAddressTypeIPv4 :
            SocketAddressTypeIPv6);
    setAccessControl(accessControl);

    // Fail early if the combination is not supported
    if ((accessControl == SocketAccessControlLocalhost &&
            addressType() == SocketAddressTypeIPv4 &&
            !supportsIPv4SocketsOnLocalhost()) ||
        (accessControl == SocketAccessControlPort &&
            addressType() == SocketAddressTypeIPv4 &&
            !supportsIPv4SocketsWithSpecifiedAddress()) ||
        (accessControl == SocketAccessControlLocalhost &&
            addressType() == SocketAddressTypeIPv6 &&
            !supportsIPv6SocketsOnLocalhost()) ||
        (accessControl == SocketAccessControlPort &&
            addressType() == SocketAddressTypeIPv6 &&
            !supportsIPv6SocketsWithSpecifiedAddress())) {
        warning() << "You requested an address type/access control combination "
                "not supported by this channel";
        return new PendingStreamTubeConnection(TP_QT_ERROR_NOT_IMPLEMENTED,
                QLatin1String("The requested address type/access control "
                              "combination is not supported"),
                IncomingStreamTubeChannelPtr(this));
    }

    // Perform the actual call
    PendingVariant *pv = new PendingVariant(
            interface<Client::ChannelTypeStreamTubeInterface>()->Accept(
                    addressType(),
                    accessControl,
                    QDBusVariant(controlParameter)),
            IncomingStreamTubeChannelPtr(this));

    PendingStreamTubeConnection *op = new PendingStreamTubeConnection(pv, addressType(),
            false, 0, IncomingStreamTubeChannelPtr(this));
    return op;
}
Ejemplo n.º 7
0
void linkInstructionBranches(Instructions &instructions) {
	/* Go through all instructions and link them according to the flow graph.
	 *
	 * In specifics, link each instruction's follower, the instruction that
	 * naturally follows if no branches are taken. Also fill in the branches
	 * array, which contains all branches an instruction can take. This
	 * directly creates an address type for each instruction: does it start
	 * a subroutine, is it a jump destination, is it a tail of a jump or none
	 * of these?
	 */

	for (Instructions::iterator i = instructions.begin(); i != instructions.end(); ++i) {
		// If this is an instruction that has a natural follower, link it
		if ((i->opcode != kOpcodeJMP) && (i->opcode != kOpcodeRETN)) {
			Instructions::iterator follower = i + 1;

			i->follower = (follower != instructions.end()) ? &*follower : 0;

			if (follower != instructions.end())
				follower->predecessors.push_back(&*i);
		}

		// Link destinations of unconditional branches
		if ((i->opcode == kOpcodeJMP) || (i->opcode == kOpcodeJSR) || (i->opcode == kOpcodeSTORESTATE)) {
			assert(((i->opcode == kOpcodeSTORESTATE) && (i->argCount == 3)) || (i->argCount == 1));

			Instruction *branch = findInstruction(instructions, i->address + i->args[0]);
			if (!branch)
				throw Common::Exception("Can't find destination of unconditional branch");

			i->branches.push_back(branch);

			if      (i->opcode == kOpcodeJSR)
				setAddressType(branch, kAddressTypeSubRoutine);
			else if (i->opcode == kOpcodeSTORESTATE)
				setAddressType(branch, kAddressTypeStoreState);
			else {
				setAddressType(branch, kAddressTypeJumpLabel);
				branch->predecessors.push_back(&*i);
			}

			setAddressType(const_cast<Instruction *>(i->follower), kAddressTypeTail);
		}

		// Link destinations of conditional branches
		if ((i->opcode == kOpcodeJZ) || (i->opcode == kOpcodeJNZ)) {
			assert(i->argCount == 1);

			if (!i->follower)
				throw Common::Exception("Conditional branch has no false destination");

			Instruction *branch = findInstruction(instructions, i->address + i->args[0]);
			if (!branch)
				throw Common::Exception("Can't find destination of conditional branch");

			setAddressType(branch, kAddressTypeJumpLabel);

			setAddressType(const_cast<Instruction *>(i->follower), kAddressTypeTail);

			i->branches.push_back(branch);      // True branch
			i->branches.push_back(i->follower); // False branch

			branch->predecessors.push_back(&*i);
		}
	}
}