<commit_before>#include <set>
#include "Game/Physics/MergeBuilder.h"

#ifdef DEBUG
#define DEBUG_MAPSPEW
#endif
#ifdef DEBUG_MAPSPEW
#include <iostream>
#include <sstream>
#endif

using namespace Wulf;
using namespace Wulf::Physics;

bool notMergable(TileType type)
{
	return type == TileType::Door || type == TileType::Pickup;
}

MergeNode::MergeNode(Map::Node const& node)
	: topLeft(node.x, node.y), type(NodeToTile(node))
	, width(0), height(0)
	, done(false)
	, mTileData(nullptr)
{
}

bool MergeNode::compatible(MergeNode const* other) const
{
	if (other == nullptr || other->done || other->type != type || notMergable(other->type))
		return false;
	return true;
}

// This returns the tiledata for the merged mass.
// It is safe to call multiple times as it only returns one TileData.
TileData* MergeNode::toTile()
{
	if (mTileData != nullptr)
		return mTileData;
	// Because short + short coerces to int if left to itself.
	coord brx = topLeft.x + width, bry = topLeft.y + height;
	mTileData = new TileData(topLeft, coords(brx, bry), type);
	return mTileData;
}

std::tuple<std::string, std::string, std::string> MergeNode::toString(coords tile) const
{
#ifdef DEBUG_MAPSPEW
	std::string contents;
	if (type == TileType::Empty) {
		contents = ".";
	} else if (type == TileType::Pickup) {
		contents = "x";
	} else if (type == TileType::Sprite) {
		contents = "~";
	} else if (type == TileType::Door) {
		contents = "D";
	} else {
		contents = "#";
	}

	std::stringstream a, b, c;
	coords bottomRight(topLeft.x - width, topLeft.y + height);
	if (tile == topLeft) {
		a << "/-";
		b << "|";
	} else if (tile.x == topLeft.x) {
		a << "|" << contents;
		b << "|";
	} else if (tile.y == topLeft.y) {
		a << "--";
		b << contents;
	} else {
		a << contents << contents;
		b << contents;
	}
	b << contents;
	if (tile.y == topLeft.y) {
		if (tile.x == bottomRight.x) {
			a << "\\";
			b << "|";
		} else {
			a << "-";
			b << contents;
		}
	} else if (tile.x == bottomRight.x) {
		a << "|";
		b << "|";
	} else {
		a << contents;
		b << contents;
	}
	if (tile.x == topLeft.x) {
		if (tile.y == bottomRight.y) {
			c << "\\-";
		} else {
			c << "|" << contents;
		}
	} else if (tile.y == bottomRight.y) {
		c << "--";
	} else {
		c << contents << contents;
	}
	if (tile == bottomRight) {
		c << "/";
	} else if (tile.x == bottomRight.x) {
		c << "|";
	} else if (tile.y == bottomRight.y) {
		c << "-";
	} else {
		c << contents;
	}
	return std::make_tuple(a.str(), b.str(), c.str());
#else
	return std::make_tuple("", "", "");
#endif
}

MergeBuilder::MergeBuilder(Map::Map const& map)
{
	for (auto& xnodes : nodes)
		std::fill(xnodes.begin(), xnodes.end(), nullptr);
	loadMap(map);
	performMerges();
}
MergeBuilder::~MergeBuilder()
{
	// Use a set because we have duplicate pointers
	std::set<MergeNode*> pointrs;
	for (auto& xnodes : nodes) {
		pointrs.insert(xnodes.begin(), xnodes.end());
	}
	for (MergeNode* node : pointrs) {
		delete node;
	}
}

inline
void MergeBuilder::loadMap(Map::Map const& map)
{
	const auto& mapNodes = map.nodes;
	for (int x = 0; x < xsize; x++) {
		const auto& xnodes = mapNodes[x];
		for (int y = 0; y < ysize; y++) {
			const auto& node = xnodes[y];
			// Filter out walls the player can never hit
			if (node.wall && !node.visibleWall) {
				// Make sure corners are included
				byte i = 0;
				for (Map::Node *neighbour : node.neighbours) {
					if (neighbour != nullptr && neighbour->visibleWall) {
						i++;
					}
				}
				if (i < 2)
					continue;
			}
			nodes[x][y] = new MergeNode(node);
		}
	}
}
inline
int MergeBuilder::verticalMerge(int x, int y)
{
	MergeNode *node = nodes[x][y];
	int i = 1;
	while (y + i < ysize) { // Ensure we stay within bounds
		MergeNode *other = nodes[x][y + i];
		// Check if the node is the same kind as us. (includes a nullptr check)
		if (!node->compatible(other))
			break;

		// Eat the node
		node->height++;
		delete other;
		nodes[x][y + i] = node;

		i++;
	}
	return i - 1;
}

inline
void MergeBuilder::horizontalMerge(int x, int y)
{
	MergeNode *node = nodes[x][y];
	int i = 1;
	// Consume horizontally
	while (x + i < xsize) {
		// Make sure that we can consume all x tiles for our height
		for (int j = 0; j <= node->height; j++) {
			MergeNode *other = nodes[x + i][y + j];
			if (!node->compatible(other)) {
				return;
			}
		}
		node->width++;
		// Eat all nodes for our height
		for (int j = 0; j <= node->height; j++) {
			delete nodes[x + i][y + j];
			nodes[x + i][y + j] = node;
		}

		i++;
	}
}

inline
void MergeBuilder::performMerges()
{
	for (int x = 0; x < xsize; x++) {
		for (int y = 0; y < ysize; y++) {
			MergeNode *node = nodes[x][y];
			if (node == nullptr || node->done || notMergable(node->type))
				continue;
			// Merge vertically first due to how the loop is layed out.
			int yadd = verticalMerge(x, y);
			horizontalMerge(x, y);
			node->done = true;
			// yadd lets us skip consumed nodes for the next iteration of y.
			y += yadd;
		}
	}
}

std::vector<std::pair<coords, TileData*>> MergeBuilder::getTileData() const
{
	std::vector<std::pair<coords, TileData*>> ret;
	ret.reserve(xsize * ysize);
	const coord xhalf = Map::Map::halfwidth;
	const coord yhalf = Map::Map::halfheight;

	// SPAM
	DebugOutput();

	for (coord x = 0; x < xsize; x++) {
		for (coord y = 0; y < ysize; y++) {
			MergeNode* node = nodes[x][y];
			if (node == nullptr)
				continue;
			coord nodeX = -(x - xhalf); // I don't know why this is negative, but it is.
			coord nodeY = y - yhalf;
			ret.emplace_back(coords(nodeX, nodeY), node->toTile());
		}
	}

	return ret;
}

void MergeBuilder::DebugOutput() const
{
#ifdef DEBUG_MAPSPEW
	const coord xhalf = Map::Map::halfwidth;
	const coord yhalf = Map::Map::halfheight;

	coord xcoord, ycoord;

	std::cout << " vim: nowrap listchars=" << std::endl;
	std::cout.setf(std::ios_base::right);
	std::cout.fill(' ');
	std::cout.width(3);
	std::cout << "  ";
	std::ostringstream bluh;
	bluh << "   ";
	for (byte x = 0; x < xsize; ++x) {
		std::cout.width(3);
		std::cout << -(x - xhalf);
		bluh << "***";
	}
	std::cout << std::endl;
	std::cout << bluh.str() << std::endl;
	for (byte y = 0; y < ysize; ++y) {
		ycoord = (y - yhalf);
		std::stringstream a, b, c;
		a << "   *";
		b.width(3);
		b << ycoord << '*';
		c << "   *";
		for (byte x = 0; x < xsize; ++x) {
			xcoord = -(x - xhalf);
			MergeNode *node = nodes[x][y];
			if (node == nullptr) {
				a << "   ";
				b << "   ";
				c << "   ";
				continue;
			}
			auto lines = node->toString(coords(xcoord, ycoord));
			a << std::get<0>(lines);
			b << std::get<1>(lines);
			c << std::get<2>(lines);
		}
		std::cout << a.str() << std::endl;
		std::cout << b.str() << std::endl;
		std::cout << c.str() << std::endl;
	}
#endif
}
<commit_msg>Make the horizontal walls show up the right length in the debug overlay<commit_after>#include <set>
#include "Game/Physics/MergeBuilder.h"

#ifdef DEBUG
//#define DEBUG_MAPSPEW
#endif
#ifdef DEBUG_MAPSPEW
#include <iostream>
#include <sstream>
#endif

using namespace Wulf;
using namespace Wulf::Physics;

bool notMergable(TileType type)
{
	return type == TileType::Door || type == TileType::Pickup;
}

MergeNode::MergeNode(Map::Node const& node)
	: topLeft(node.x, node.y), type(NodeToTile(node))
	, width(0), height(0)
	, done(false)
	, mTileData(nullptr)
{
}

bool MergeNode::compatible(MergeNode const* other) const
{
	if (other == nullptr || other->done || other->type != type || notMergable(other->type))
		return false;
	return true;
}

// This returns the tiledata for the merged mass.
// It is safe to call multiple times as it only returns one TileData.
TileData* MergeNode::toTile()
{
	if (mTileData != nullptr)
		return mTileData;
	// Because short + short coerces to int if left to itself.
	coord width = this->width;
	coords topLeft = this->topLeft;
	if (width > 0) {
		width += 2;
		topLeft.x += 1;
	}
	coord brx = topLeft.x - width, bry = topLeft.y + height;
	mTileData = new TileData(topLeft, coords(brx, bry), type);
	return mTileData;
}

std::tuple<std::string, std::string, std::string> MergeNode::toString(coords tile) const
{
#ifdef DEBUG_MAPSPEW
	std::string contents;
	if (type == TileType::Empty) {
		contents = ".";
	} else if (type == TileType::Pickup) {
		contents = "x";
	} else if (type == TileType::Sprite) {
		contents = "~";
	} else if (type == TileType::Door) {
		contents = "D";
	} else {
		contents = "#";
	}

	std::stringstream a, b, c;
	coords bottomRight(topLeft.x - width, topLeft.y + height);
	if (tile == topLeft) {
		a << "/-";
		b << "|";
	} else if (tile.x == topLeft.x) {
		a << "|" << contents;
		b << "|";
	} else if (tile.y == topLeft.y) {
		a << "--";
		b << contents;
	} else {
		a << contents << contents;
		b << contents;
	}
	b << contents;
	if (tile.y == topLeft.y) {
		if (tile.x == bottomRight.x) {
			a << "\\";
			b << "|";
		} else {
			a << "-";
			b << contents;
		}
	} else if (tile.x == bottomRight.x) {
		a << "|";
		b << "|";
	} else {
		a << contents;
		b << contents;
	}
	if (tile.x == topLeft.x) {
		if (tile.y == bottomRight.y) {
			c << "\\-";
		} else {
			c << "|" << contents;
		}
	} else if (tile.y == bottomRight.y) {
		c << "--";
	} else {
		c << contents << contents;
	}
	if (tile == bottomRight) {
		c << "/";
	} else if (tile.x == bottomRight.x) {
		c << "|";
	} else if (tile.y == bottomRight.y) {
		c << "-";
	} else {
		c << contents;
	}
	return std::make_tuple(a.str(), b.str(), c.str());
#else
	return std::make_tuple("", "", "");
#endif
}

MergeBuilder::MergeBuilder(Map::Map const& map)
{
	for (auto& xnodes : nodes)
		std::fill(xnodes.begin(), xnodes.end(), nullptr);
	loadMap(map);
	performMerges();
}
MergeBuilder::~MergeBuilder()
{
	// Use a set because we have duplicate pointers
	std::set<MergeNode*> pointrs;
	for (auto& xnodes : nodes) {
		pointrs.insert(xnodes.begin(), xnodes.end());
	}
	for (MergeNode* node : pointrs) {
		delete node;
	}
}

inline
void MergeBuilder::loadMap(Map::Map const& map)
{
	const auto& mapNodes = map.nodes;
	for (int x = 0; x < xsize; x++) {
		const auto& xnodes = mapNodes[x];
		for (int y = 0; y < ysize; y++) {
			const auto& node = xnodes[y];
			// Filter out walls the player can never hit
			if (node.wall && !node.visibleWall) {
				// Make sure corners are included
				byte i = 0;
				for (Map::Node *neighbour : node.neighbours) {
					if (neighbour != nullptr && neighbour->visibleWall) {
						i++;
					}
				}
				if (i < 2)
					continue;
			}
			nodes[x][y] = new MergeNode(node);
		}
	}
}
inline
int MergeBuilder::verticalMerge(int x, int y)
{
	MergeNode *node = nodes[x][y];
	int i = 1;
	while (y + i < ysize) { // Ensure we stay within bounds
		MergeNode *other = nodes[x][y + i];
		// Check if the node is the same kind as us. (includes a nullptr check)
		if (!node->compatible(other))
			break;

		// Eat the node
		node->height++;
		delete other;
		nodes[x][y + i] = node;

		i++;
	}
	return i - 1;
}

inline
void MergeBuilder::horizontalMerge(int x, int y)
{
	MergeNode *node = nodes[x][y];
	int i = 1;
	// Consume horizontally
	while (x + i < xsize) {
		// Make sure that we can consume all x tiles for our height
		for (int j = 0; j <= node->height; j++) {
			MergeNode *other = nodes[x + i][y + j];
			if (!node->compatible(other)) {
				return;
			}
		}
		node->width++;
		// Eat all nodes for our height
		for (int j = 0; j <= node->height; j++) {
			delete nodes[x + i][y + j];
			nodes[x + i][y + j] = node;
		}

		i++;
	}
}

inline
void MergeBuilder::performMerges()
{
	for (int x = 0; x < xsize; x++) {
		for (int y = 0; y < ysize; y++) {
			MergeNode *node = nodes[x][y];
			if (node == nullptr || node->done || notMergable(node->type))
				continue;
			// Merge vertically first due to how the loop is layed out.
			int yadd = verticalMerge(x, y);
			horizontalMerge(x, y);
			node->done = true;
			// yadd lets us skip consumed nodes for the next iteration of y.
			y += yadd;
		}
	}
}

std::vector<std::pair<coords, TileData*>> MergeBuilder::getTileData() const
{
	std::vector<std::pair<coords, TileData*>> ret;
	ret.reserve(xsize * ysize);
	const coord xhalf = Map::Map::halfwidth;
	const coord yhalf = Map::Map::halfheight;

	// SPAM
	DebugOutput();

	for (coord x = 0; x < xsize; x++) {
		for (coord y = 0; y < ysize; y++) {
			MergeNode* node = nodes[x][y];
			if (node == nullptr)
				continue;
			coord nodeX = -(x - xhalf); // I don't know why this is negative, but it is.
			coord nodeY = y - yhalf;
			ret.emplace_back(coords(nodeX, nodeY), node->toTile());
		}
	}

	return ret;
}

void MergeBuilder::DebugOutput() const
{
#ifdef DEBUG_MAPSPEW
	const coord xhalf = Map::Map::halfwidth;
	const coord yhalf = Map::Map::halfheight;

	coord xcoord, ycoord;

	std::cout << " vim: nowrap listchars=" << std::endl;
	std::cout.setf(std::ios_base::right);
	std::cout.fill(' ');
	std::cout.width(3);
	std::cout << "  ";
	std::ostringstream bluh;
	bluh << "   ";
	for (byte x = 0; x < xsize; ++x) {
		std::cout.width(3);
		std::cout << -(x - xhalf);
		bluh << "***";
	}
	std::cout << std::endl;
	std::cout << bluh.str() << std::endl;
	for (byte y = 0; y < ysize; ++y) {
		ycoord = (y - yhalf);
		std::stringstream a, b, c;
		a << "   *";
		b.width(3);
		b << ycoord << '*';
		c << "   *";
		for (byte x = 0; x < xsize; ++x) {
			xcoord = -(x - xhalf);
			MergeNode *node = nodes[x][y];
			if (node == nullptr) {
				a << "   ";
				b << "   ";
				c << "   ";
				continue;
			}
			auto lines = node->toString(coords(xcoord, ycoord));
			a << std::get<0>(lines);
			b << std::get<1>(lines);
			c << std::get<2>(lines);
		}
		std::cout << a.str() << std::endl;
		std::cout << b.str() << std::endl;
		std::cout << c.str() << std::endl;
	}
#endif
}
<|endoftext|>


<commit_before>//===--- GenClangType.cpp - Swift IR Generation For Types -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements generation of Clang AST types from Swift AST types
//  for types that are representable in Objective-C interfaces.
//
//===----------------------------------------------------------------------===//

#include "GenClangType.h"

#include "llvm/ADT/StringSwitch.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/Type.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Type.h"

using namespace swift;
using namespace irgen;

static Type getNamedSwiftType(DeclContext *DC, StringRef name) {
  assert(DC && "Unexpected null declaration context!");

  auto &astContext = DC->getASTContext();
  UnqualifiedLookup lookup(astContext.getIdentifier(name), DC, nullptr);
  if (auto type = lookup.getSingleTypeResult())
    return type->getDeclaredType();

  return Type();
}

static const clang::CanQualType getClangBuiltinTypeFromKind(
  const clang::ASTContext &context,
  clang::BuiltinType::Kind kind) {
  switch (kind) {
#define BUILTIN_TYPE(Id, SingletonId) \
  case clang::BuiltinType::Id: return context.SingletonId;
#include "clang/AST/BuiltinTypes.def"
  }
}

static clang::CanQualType getUnhandledType() {
  return clang::CanQualType();
}

static clang::CanQualType getClangSelectorType(
  const clang::ASTContext &clangCtx) {
  return clangCtx.getPointerType(clangCtx.ObjCBuiltinSelTy);
}

static clang::CanQualType getClangMetatypeType(
  const clang::ASTContext &clangCtx) {
  clang::QualType clangType =
    clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinClassTy, 0, 0);
  clangType = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(clangType);
}

static clang::CanQualType getClangIdType(
  const clang::ASTContext &clangCtx) {
  clang::QualType clangType =
    clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, 0, 0);
  clangType = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(clangType);
}

static clang::CanQualType getClangVaListType(
  const clang::ASTContext &clangCtx) {
  return clangCtx.getCanonicalType(clangCtx.getBuiltinVaListType());
}

clang::CanQualType GenClangType::visitStructType(CanStructType type) {
  // First attempt a lookup in our map of imported structs.
  auto *decl = type->getDecl();
  if (auto *clangDecl = decl->getClangDecl()) {
    auto *typeDecl = cast<clang::TypeDecl>(clangDecl);
    auto &clangCtx = getClangASTContext();
    auto clangType = clangCtx.getTypeDeclType(typeDecl);
    return clangCtx.getCanonicalType(clangType);
  }

  auto const &clangCtx = getClangASTContext();

#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) {                \
    auto lookupTy = getNamedSwiftType(decl->getDeclContext(),                  \
                                      #SWIFT_TYPE_NAME);                       \
    if (lookupTy && lookupTy->isEqual(type))                                   \
      return getClangBuiltinTypeFromKind(clangCtx,                             \
                                      clang::BuiltinType::CLANG_BUILTIN_KIND); \
  }
#include "swift/ClangImporter/BuiltinMappedTypes.def"
#undef MAP_BUILTIN_TYPE

  // Handle other imported types.

#define CHECK_CLANG_TYPE_MATCH(TYPE, NAME, RESULT)                           \
  if (auto lookupTy = getNamedSwiftType((TYPE)->getDecl()->getDeclContext(), \
                                        (NAME)))                             \
    if (lookupTy->isEqual(type))                                             \
      return (RESULT);

  CHECK_CLANG_TYPE_MATCH(type, "COpaquePointer", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "CConstVoidPointer",
                     clangCtx.getCanonicalType(clangCtx.VoidPtrTy.withConst()));
  CHECK_CLANG_TYPE_MATCH(type, "CMutableVoidPointer", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "ObjCBool", clangCtx.ObjCBuiltinBoolTy);
  // FIXME: This is sufficient for ABI type generation, but should
  //        probably be const char* for type encoding.
  CHECK_CLANG_TYPE_MATCH(type, "CString", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "Selector", getClangSelectorType(clangCtx));
  // We import NZone* (a struct pointer) as NSZone.
  CHECK_CLANG_TYPE_MATCH(type, "NSZone", clangCtx.VoidPtrTy);
  // We import NSString* (an Obj-C object pointer) as String.
  CHECK_CLANG_TYPE_MATCH(type, "String", getClangIdType(getClangASTContext()));
  CHECK_CLANG_TYPE_MATCH(type, "CVaListPointer", getClangVaListType(clangCtx));
#undef CHECK_CLANG_TYPE_MATCH

  llvm_unreachable("Unhandled struct type in Clang type generation");
  return getUnhandledType();
}

clang::CanQualType GenClangType::visitTupleType(CanTupleType type) {
  if (type->getNumElements() == 0)
    return getClangASTContext().VoidTy;

  llvm_unreachable("Unexpected tuple type in Clang type generation!");
  return getUnhandledType();
}

clang::CanQualType GenClangType::visitProtocolType(CanProtocolType type) {
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) {
  return getClangMetatypeType(getClangASTContext());
}

clang::CanQualType
GenClangType::visitExistentialMetatypeType(CanExistentialMetatypeType type) {
  return getClangMetatypeType(getClangASTContext());
}

clang::CanQualType GenClangType::visitClassType(CanClassType type) {
  auto &clangCtx = getClangASTContext();
  // produce the clang type INTF * if it is imported ObjC object.
  if (auto *swiftDecl = type->getDecl()) {
    if (auto *clangDecl = swiftDecl->getClangDecl()) {
      if (auto *CDecl = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) {
        auto clangType  = clangCtx.getObjCInterfaceType(CDecl);
        auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
        return clangCtx.getCanonicalType(ptrTy);
      }
    }
    else if (swiftDecl->isObjC()) {
      clang::IdentifierInfo *ForwardClassId =
        &clangCtx.Idents.get(swiftDecl->getName().get());
      if (auto *CDecl = clang::ObjCInterfaceDecl::Create(
                          clangCtx, clangCtx.getTranslationUnitDecl(),
                          clang::SourceLocation(), ForwardClassId,
                          0/*PrevIDecl*/, clang::SourceLocation())) {
        auto clangType  = clangCtx.getObjCInterfaceType(CDecl);
        auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
        return clangCtx.getCanonicalType(ptrTy);
      }
    }
  }
  return getClangIdType(clangCtx);
}

clang::CanQualType GenClangType::visitBoundGenericClassType(
                                                CanBoundGenericClassType type) {
  // Any @objc class type in Swift that shows up in an @objc method maps 1-1 to
  // "id <SomeProto>"; with clang's encoding ignoring the protocol list.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType
GenClangType::visitBoundGenericType(CanBoundGenericType type) {
  // We only expect *Pointer<T>, ImplicitlyUnwrappedOptional<T>, and Optional<T>.
  // The first two are structs; the last is an enum.
  if (auto underlyingTy = type.getAnyOptionalObjectType()) {
    // The underlying type could be a bridged type, which makes any
    // sort of casual assertion here difficult.
    return visit(underlyingTy);
  }

  auto swiftStructDecl = type->getDecl();

  enum class StructKind {
    Invalid,
    UnsafePointer,
    ObjCMutablePointer,
    CMutablePointer,
    CConstPointer,
    Array,
    Unmanaged,
  } kind = llvm::StringSwitch<StructKind>(swiftStructDecl->getName().str())
    .Case("UnsafePointer", StructKind::UnsafePointer)
    .Case("ObjCMutablePointer", StructKind::ObjCMutablePointer)
    .Case("CMutablePointer", StructKind::CMutablePointer)
    .Case("CConstPointer", StructKind::CConstPointer)
    .Case("Array", StructKind::Array)
    .Case("Unmanaged", StructKind::Unmanaged)
    .Default(StructKind::Invalid);

  auto args = type->getGenericArgs();
  assert(args.size() == 1 &&
         "*Pointer<T> should have a single generic argument!");

  switch (kind) {
  case StructKind::Invalid:
    llvm_unreachable("Unexpected non-pointer generic struct type in imported"
                     " Clang module!");

  case StructKind::UnsafePointer: // Assume UnsafePointer is mutable
  case StructKind::Unmanaged:
  case StructKind::ObjCMutablePointer:
  case StructKind::CMutablePointer: {
    auto clangCanTy = visit(args.front()->getCanonicalType());
    return getClangASTContext().getPointerType(clangCanTy);
  }

  case StructKind::CConstPointer:
  case StructKind::Array:
    {
    clang::QualType clangTy
      = visit(args.front()->getCanonicalType()).withConst();
    return getClangASTContext().getCanonicalType(
                                  getClangASTContext().getPointerType(clangTy));
  }
  }
}

clang::CanQualType GenClangType::visitEnumType(CanEnumType type) {
  auto *clangDecl = type->getDecl()->getClangDecl();
  assert(clangDecl && "Imported enum without Clang decl!");

  auto &clangCtx = getClangASTContext();
  auto *typeDecl = cast<clang::TypeDecl>(clangDecl);
  auto clangType = clangCtx.getTypeDeclType(typeDecl);
  return clangCtx.getCanonicalType(clangType);
}

// FIXME: We hit this building Foundation, with a call on the type
//        encoding path. It seems like we shouldn't see FunctionType
//        at that point.
clang::CanQualType GenClangType::visitFunctionType(CanFunctionType type) {
  auto &clangCtx = getClangASTContext();
  GenClangType CTG(Context);
  SmallVector<clang::QualType, 16> ParamTypes;
  Type Result = type->getResult();
  Type Input = type->getInput();
  auto ResultType = CTG.visit(Result->getCanonicalType());
  if (!ResultType.isNull())
    if (auto tuple = dyn_cast<TupleType>(Input->getCanonicalType())) {
      bool failed = false;
      for (unsigned i = 0; i < tuple->getNumElements(); i++) {
        Type ArgType = tuple->getElementType(i);
        auto clangType = CTG.visit(ArgType->getCanonicalType());
        if (clangType.isNull()) {
          failed = true;
          break;
        }
        ParamTypes.push_back(clangType);
      }
      if (!failed) {
        clang::FunctionProtoType::ExtProtoInfo DefaultEPI;
        auto fnTy = clangCtx.getFunctionType(ResultType, ParamTypes, DefaultEPI);
        auto blockTy = clangCtx.getBlockPointerType(fnTy);
        return clangCtx.getCanonicalType(blockTy);
      }
    }

  // We'll select (void)(^)() for function types. As long as it's a
  // pointer type it doesn't matter exactly which for either ABI type
  // generation or Obj-C type encoding.
  auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy);
  auto blockTy = clangCtx.getBlockPointerType(fnTy);
  return clangCtx.getCanonicalType(blockTy);
}

clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) {
  // We'll select (void)(^)() for function types. As long as it's a
  // pointer type it doesn't matter exactly which for either ABI type
  // generation or Obj-C type encoding.
  //
  // FIXME: This isn't strictly true because ObjC extended method encoding
  // encodes the parameter and return types of blocks.
  auto &clangCtx = getClangASTContext();
  auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy);
  auto blockTy = clangCtx.getBlockPointerType(fnTy);
  return clangCtx.getCanonicalType(blockTy);
}

clang::CanQualType GenClangType::visitSILBlockStorageType(CanSILBlockStorageType type) {
  // We'll select (void)(^)() for function types. As long as it's a
  // pointer type it doesn't matter exactly which for either ABI type
  // generation or Obj-C type encoding.
  auto &clangCtx = getClangASTContext();
  auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy);
  auto blockTy = clangCtx.getBlockPointerType(fnTy);
  return clangCtx.getCanonicalType(blockTy);
}

clang::CanQualType GenClangType::visitProtocolCompositionType(
  CanProtocolCompositionType type) {
  // FIXME. Eventually, this will have its own helper routine.
  SmallVector<const clang::ObjCProtocolDecl *, 4> Protocols;
  for (Type t : type->getProtocols()) {
    ProtocolDecl *protocol = t->castTo<ProtocolType>()->getDecl();
    if (auto *clangDecl = protocol->getClangDecl())
      if (auto *PDecl = dyn_cast<clang::ObjCProtocolDecl>(clangDecl))
        Protocols.push_back(PDecl);
  }
  auto &clangCtx = getClangASTContext();
  if (Protocols.empty())
    return getClangIdType(clangCtx);
  // id<protocol-list>
  clang::ObjCProtocolDecl **ProtoQuals = new(clangCtx) clang::ObjCProtocolDecl*[Protocols.size()];
  memcpy(ProtoQuals, Protocols.data(), sizeof(clang::ObjCProtocolDecl*)*Protocols.size());
  auto clangType = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy,
                     ProtoQuals,
                     Protocols.size());
  auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(ptrTy);
}

clang::CanQualType GenClangType::visitBuiltinRawPointerType(
  CanBuiltinRawPointerType type) {
  return getClangASTContext().VoidPtrTy;
}

clang::CanQualType GenClangType::visitBuiltinUnknownObjectType(
  CanBuiltinUnknownObjectType type) {
  auto &clangCtx = getClangASTContext();
  auto ptrTy = clangCtx.getObjCObjectPointerType(clangCtx.VoidTy);
  return clangCtx.getCanonicalType(ptrTy);
}

clang::CanQualType GenClangType::visitArchetypeType(CanArchetypeType type) {
  // We see these in the case where we invoke an @objc function
  // through a protocol.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitDynamicSelfType(CanDynamicSelfType type) {
  // Dynamic Self is equivalent to 'instancetype', which is treated as
  // 'id' within the Objective-C type system.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitGenericTypeParamType(
  CanGenericTypeParamType type) {
  // We see these in the case where we invoke an @objc function
  // through a protocol argument that is a generic type.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitType(CanType type) {
  llvm_unreachable("Unexpected type in Clang type generation.");
  return getUnhandledType();
}

const clang::ASTContext &GenClangType::getClangASTContext() const {
  auto *CI = static_cast<ClangImporter*>(&*Context.getClangModuleLoader());
  return CI->getClangASTContext();
}
<commit_msg>Revert "Teach the swift->clang ABI code not to sweat about"<commit_after>//===--- GenClangType.cpp - Swift IR Generation For Types -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements generation of Clang AST types from Swift AST types
//  for types that are representable in Objective-C interfaces.
//
//===----------------------------------------------------------------------===//

#include "GenClangType.h"

#include "llvm/ADT/StringSwitch.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/Type.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Type.h"

using namespace swift;
using namespace irgen;

static Type getNamedSwiftType(DeclContext *DC, StringRef name) {
  assert(DC && "Unexpected null declaration context!");

  auto &astContext = DC->getASTContext();
  UnqualifiedLookup lookup(astContext.getIdentifier(name), DC, nullptr);
  if (auto type = lookup.getSingleTypeResult())
    return type->getDeclaredType();

  return Type();
}

static const clang::CanQualType getClangBuiltinTypeFromKind(
  const clang::ASTContext &context,
  clang::BuiltinType::Kind kind) {
  switch (kind) {
#define BUILTIN_TYPE(Id, SingletonId) \
  case clang::BuiltinType::Id: return context.SingletonId;
#include "clang/AST/BuiltinTypes.def"
  }
}

static clang::CanQualType getUnhandledType() {
  return clang::CanQualType();
}

static clang::CanQualType getClangSelectorType(
  const clang::ASTContext &clangCtx) {
  return clangCtx.getPointerType(clangCtx.ObjCBuiltinSelTy);
}

static clang::CanQualType getClangMetatypeType(
  const clang::ASTContext &clangCtx) {
  clang::QualType clangType =
    clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinClassTy, 0, 0);
  clangType = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(clangType);
}

static clang::CanQualType getClangIdType(
  const clang::ASTContext &clangCtx) {
  clang::QualType clangType =
    clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, 0, 0);
  clangType = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(clangType);
}

static clang::CanQualType getClangVaListType(
  const clang::ASTContext &clangCtx) {
  return clangCtx.getCanonicalType(clangCtx.getBuiltinVaListType());
}

clang::CanQualType GenClangType::visitStructType(CanStructType type) {
  // First attempt a lookup in our map of imported structs.
  auto *decl = type->getDecl();
  if (auto *clangDecl = decl->getClangDecl()) {
    auto *typeDecl = cast<clang::TypeDecl>(clangDecl);
    auto &clangCtx = getClangASTContext();
    auto clangType = clangCtx.getTypeDeclType(typeDecl);
    return clangCtx.getCanonicalType(clangType);
  }

  auto const &clangCtx = getClangASTContext();

#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) {                \
    auto lookupTy = getNamedSwiftType(decl->getDeclContext(),                  \
                                      #SWIFT_TYPE_NAME);                       \
    if (lookupTy && lookupTy->isEqual(type))                                   \
      return getClangBuiltinTypeFromKind(clangCtx,                             \
                                      clang::BuiltinType::CLANG_BUILTIN_KIND); \
  }
#include "swift/ClangImporter/BuiltinMappedTypes.def"
#undef MAP_BUILTIN_TYPE

  // Handle other imported types.

#define CHECK_CLANG_TYPE_MATCH(TYPE, NAME, RESULT)                           \
  if (auto lookupTy = getNamedSwiftType((TYPE)->getDecl()->getDeclContext(), \
                                        (NAME)))                             \
    if (lookupTy->isEqual(type))                                             \
      return (RESULT);

  CHECK_CLANG_TYPE_MATCH(type, "COpaquePointer", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "CConstVoidPointer",
                     clangCtx.getCanonicalType(clangCtx.VoidPtrTy.withConst()));
  CHECK_CLANG_TYPE_MATCH(type, "CMutableVoidPointer", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "ObjCBool", clangCtx.ObjCBuiltinBoolTy);
  // FIXME: This is sufficient for ABI type generation, but should
  //        probably be const char* for type encoding.
  CHECK_CLANG_TYPE_MATCH(type, "CString", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "Selector", getClangSelectorType(clangCtx));
  // We import NZone* (a struct pointer) as NSZone.
  CHECK_CLANG_TYPE_MATCH(type, "NSZone", clangCtx.VoidPtrTy);
  // We import NSString* (an Obj-C object pointer) as String.
  CHECK_CLANG_TYPE_MATCH(type, "String", getClangIdType(getClangASTContext()));
  CHECK_CLANG_TYPE_MATCH(type, "CVaListPointer", getClangVaListType(clangCtx));
#undef CHECK_CLANG_TYPE_MATCH

  llvm_unreachable("Unhandled struct type in Clang type generation");
  return getUnhandledType();
}

clang::CanQualType GenClangType::visitTupleType(CanTupleType type) {
  if (type->getNumElements() == 0)
    return getClangASTContext().VoidTy;

  llvm_unreachable("Unexpected tuple type in Clang type generation!");
  return getUnhandledType();
}

clang::CanQualType GenClangType::visitProtocolType(CanProtocolType type) {
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) {
  return getClangMetatypeType(getClangASTContext());
}

clang::CanQualType
GenClangType::visitExistentialMetatypeType(CanExistentialMetatypeType type) {
  return getClangMetatypeType(getClangASTContext());
}

clang::CanQualType GenClangType::visitClassType(CanClassType type) {
  auto &clangCtx = getClangASTContext();
  // produce the clang type INTF * if it is imported ObjC object.
  if (auto *swiftDecl = type->getDecl()) {
    if (auto *clangDecl = swiftDecl->getClangDecl()) {
      if (auto *CDecl = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) {
        auto clangType  = clangCtx.getObjCInterfaceType(CDecl);
        auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
        return clangCtx.getCanonicalType(ptrTy);
      }
    }
    else if (swiftDecl->isObjC()) {
      clang::IdentifierInfo *ForwardClassId =
        &clangCtx.Idents.get(swiftDecl->getName().get());
      if (auto *CDecl = clang::ObjCInterfaceDecl::Create(
                          clangCtx, clangCtx.getTranslationUnitDecl(),
                          clang::SourceLocation(), ForwardClassId,
                          0/*PrevIDecl*/, clang::SourceLocation())) {
        auto clangType  = clangCtx.getObjCInterfaceType(CDecl);
        auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
        return clangCtx.getCanonicalType(ptrTy);
      }
    }
  }
  return getClangIdType(clangCtx);
}

clang::CanQualType GenClangType::visitBoundGenericClassType(
                                                CanBoundGenericClassType type) {
  // Any @objc class type in Swift that shows up in an @objc method maps 1-1 to
  // "id <SomeProto>"; with clang's encoding ignoring the protocol list.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType
GenClangType::visitBoundGenericType(CanBoundGenericType type) {
  // We only expect *Pointer<T>, ImplicitlyUnwrappedOptional<T>, and Optional<T>.
  // The first two are structs; the last is an enum.
  if (auto underlyingTy = type.getAnyOptionalObjectType()) {
    // The underlying type could be a bridged type, which makes any
    // sort of casual assertion here difficult.
    return visit(underlyingTy);
  }

  auto swiftStructDecl = type->getDecl();

  enum class StructKind {
    Invalid,
    UnsafePointer,
    ObjCMutablePointer,
    CMutablePointer,
    CConstPointer,
    Array,
  } kind = llvm::StringSwitch<StructKind>(swiftStructDecl->getName().str())
    .Case("UnsafePointer", StructKind::UnsafePointer)
    .Case("ObjCMutablePointer", StructKind::ObjCMutablePointer)
    .Case("CMutablePointer", StructKind::CMutablePointer)
    .Case("CConstPointer", StructKind::CConstPointer)
    .Case("Array", StructKind::Array)
    .Default(StructKind::Invalid);

  auto args = type->getGenericArgs();
  assert(args.size() == 1 &&
         "*Pointer<T> should have a single generic argument!");

  switch (kind) {
  case StructKind::Invalid:
    llvm_unreachable("Unexpected non-pointer generic struct type in imported"
                     " Clang module!");

  case StructKind::UnsafePointer: // Assume UnsafePointer is mutable
  case StructKind::ObjCMutablePointer:
  case StructKind::CMutablePointer: {
    auto clangCanTy = visit(args.front()->getCanonicalType());
    return getClangASTContext().getPointerType(clangCanTy);
  }

  case StructKind::CConstPointer:
  case StructKind::Array:
    {
    clang::QualType clangTy
      = visit(args.front()->getCanonicalType()).withConst();
    return getClangASTContext().getCanonicalType(
                                  getClangASTContext().getPointerType(clangTy));
  }
  }
}

clang::CanQualType GenClangType::visitEnumType(CanEnumType type) {
  auto *clangDecl = type->getDecl()->getClangDecl();
  assert(clangDecl && "Imported enum without Clang decl!");

  auto &clangCtx = getClangASTContext();
  auto *typeDecl = cast<clang::TypeDecl>(clangDecl);
  auto clangType = clangCtx.getTypeDeclType(typeDecl);
  return clangCtx.getCanonicalType(clangType);
}

// FIXME: We hit this building Foundation, with a call on the type
//        encoding path. It seems like we shouldn't see FunctionType
//        at that point.
clang::CanQualType GenClangType::visitFunctionType(CanFunctionType type) {
  auto &clangCtx = getClangASTContext();
  GenClangType CTG(Context);
  SmallVector<clang::QualType, 16> ParamTypes;
  Type Result = type->getResult();
  Type Input = type->getInput();
  auto ResultType = CTG.visit(Result->getCanonicalType());
  if (!ResultType.isNull())
    if (auto tuple = dyn_cast<TupleType>(Input->getCanonicalType())) {
      bool failed = false;
      for (unsigned i = 0; i < tuple->getNumElements(); i++) {
        Type ArgType = tuple->getElementType(i);
        auto clangType = CTG.visit(ArgType->getCanonicalType());
        if (clangType.isNull()) {
          failed = true;
          break;
        }
        ParamTypes.push_back(clangType);
      }
      if (!failed) {
        clang::FunctionProtoType::ExtProtoInfo DefaultEPI;
        auto fnTy = clangCtx.getFunctionType(ResultType, ParamTypes, DefaultEPI);
        auto blockTy = clangCtx.getBlockPointerType(fnTy);
        return clangCtx.getCanonicalType(blockTy);
      }
    }

  // We'll select (void)(^)() for function types. As long as it's a
  // pointer type it doesn't matter exactly which for either ABI type
  // generation or Obj-C type encoding.
  auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy);
  auto blockTy = clangCtx.getBlockPointerType(fnTy);
  return clangCtx.getCanonicalType(blockTy);
}

clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) {
  // We'll select (void)(^)() for function types. As long as it's a
  // pointer type it doesn't matter exactly which for either ABI type
  // generation or Obj-C type encoding.
  //
  // FIXME: This isn't strictly true because ObjC extended method encoding
  // encodes the parameter and return types of blocks.
  auto &clangCtx = getClangASTContext();
  auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy);
  auto blockTy = clangCtx.getBlockPointerType(fnTy);
  return clangCtx.getCanonicalType(blockTy);
}

clang::CanQualType GenClangType::visitSILBlockStorageType(CanSILBlockStorageType type) {
  // We'll select (void)(^)() for function types. As long as it's a
  // pointer type it doesn't matter exactly which for either ABI type
  // generation or Obj-C type encoding.
  auto &clangCtx = getClangASTContext();
  auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy);
  auto blockTy = clangCtx.getBlockPointerType(fnTy);
  return clangCtx.getCanonicalType(blockTy);
}

clang::CanQualType GenClangType::visitProtocolCompositionType(
  CanProtocolCompositionType type) {
  // FIXME. Eventually, this will have its own helper routine.
  SmallVector<const clang::ObjCProtocolDecl *, 4> Protocols;
  for (Type t : type->getProtocols()) {
    ProtocolDecl *protocol = t->castTo<ProtocolType>()->getDecl();
    if (auto *clangDecl = protocol->getClangDecl())
      if (auto *PDecl = dyn_cast<clang::ObjCProtocolDecl>(clangDecl))
        Protocols.push_back(PDecl);
  }
  auto &clangCtx = getClangASTContext();
  if (Protocols.empty())
    return getClangIdType(clangCtx);
  // id<protocol-list>
  clang::ObjCProtocolDecl **ProtoQuals = new(clangCtx) clang::ObjCProtocolDecl*[Protocols.size()];
  memcpy(ProtoQuals, Protocols.data(), sizeof(clang::ObjCProtocolDecl*)*Protocols.size());
  auto clangType = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy,
                     ProtoQuals,
                     Protocols.size());
  auto ptrTy = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(ptrTy);
}

clang::CanQualType GenClangType::visitBuiltinRawPointerType(
  CanBuiltinRawPointerType type) {
  return getClangASTContext().VoidPtrTy;
}

clang::CanQualType GenClangType::visitBuiltinUnknownObjectType(
  CanBuiltinUnknownObjectType type) {
  auto &clangCtx = getClangASTContext();
  auto ptrTy = clangCtx.getObjCObjectPointerType(clangCtx.VoidTy);
  return clangCtx.getCanonicalType(ptrTy);
}

clang::CanQualType GenClangType::visitArchetypeType(CanArchetypeType type) {
  // We see these in the case where we invoke an @objc function
  // through a protocol.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitDynamicSelfType(CanDynamicSelfType type) {
  // Dynamic Self is equivalent to 'instancetype', which is treated as
  // 'id' within the Objective-C type system.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitGenericTypeParamType(
  CanGenericTypeParamType type) {
  // We see these in the case where we invoke an @objc function
  // through a protocol argument that is a generic type.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitType(CanType type) {
  llvm_unreachable("Unexpected type in Clang type generation.");
  return getUnhandledType();
}

const clang::ASTContext &GenClangType::getClangASTContext() const {
  auto *CI = static_cast<ClangImporter*>(&*Context.getClangModuleLoader());
  return CI->getClangASTContext();
}
<|endoftext|>


<commit_before>//===--- GenClangType.cpp - Swift IR Generation For Types -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements generation of Clang AST types from Swift AST types
//  for types that are representable in Objective-C interfaces.
//
//===----------------------------------------------------------------------===//

#include "GenClangType.h"

#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/Type.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"

using namespace swift;
using namespace irgen;

static Type getNamedSwiftType(DeclContext *DC, StringRef name) {
  assert(DC && "Unexpected null declaration context!");

  auto &astContext = DC->getASTContext();
  UnqualifiedLookup lookup(astContext.getIdentifier(name), DC, nullptr);
  if (auto type = lookup.getSingleTypeResult())
    return type->getDeclaredType();

  return Type();
}

static const clang::CanQualType getClangBuiltinTypeFromKind(
  const clang::ASTContext &context,
  clang::BuiltinType::Kind kind) {
  switch (kind) {
#define BUILTIN_TYPE(Id, SingletonId) \
  case clang::BuiltinType::Id: return context.SingletonId;
#include "clang/AST/BuiltinTypes.def"
  }
}

static clang::CanQualType getClangSelectorType(
  const clang::ASTContext &clangCtx) {
  return clangCtx.getPointerType(clangCtx.ObjCBuiltinSelTy);
}

clang::CanQualType GenClangType::visitStructType(CanStructType type) {
  // First attempt a lookup in our map of imported structs.
  auto *decl = type->getDecl();
  if (auto *clangDecl = decl->getClangDecl()) {
    auto *typeDecl = cast<clang::TypeDecl>(clangDecl);
    return typeDecl->getTypeForDecl()->getCanonicalTypeUnqualified();
  }

  auto const &clangCtx = getClangASTContext();

#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) {                \
    auto lookupTy = getNamedSwiftType(decl->getDeclContext(),                  \
                                      #SWIFT_TYPE_NAME);                       \
    if (lookupTy && lookupTy->isEqual(type))                                   \
      return getClangBuiltinTypeFromKind(clangCtx,                             \
                                      clang::BuiltinType::CLANG_BUILTIN_KIND); \
  }
#include "swift/ClangImporter/BuiltinMappedTypes.def"
#undef MAP_BUILTIN_TYPE

  // Handle other imported types.

#define CHECK_CLANG_TYPE_MATCH(TYPE, NAME, RESULT)                           \
  if (auto lookupTy = getNamedSwiftType((TYPE)->getDecl()->getDeclContext(), \
                                        (NAME)))                             \
    if (lookupTy->isEqual(type))                                             \
      return (RESULT);

  CHECK_CLANG_TYPE_MATCH(type, "COpaquePointer", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "Bool", clangCtx.SignedCharTy);
  CHECK_CLANG_TYPE_MATCH(type, "ObjCBool", clangCtx.ObjCBuiltinBoolTy);
  // FIXME: This is sufficient for ABI type generation, but should
  //        probably be const char* for type encoding.
  CHECK_CLANG_TYPE_MATCH(type, "CString", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "Selector", getClangSelectorType(clangCtx));
#undef CHECK_CLANG_TYPE_MATCH
  // FIXME: Handle other structs resulting from imported non-struct Clang types.
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitTupleType(CanTupleType type) {
  if (type->getNumElements() == 0)
    return getClangASTContext().VoidTy;

  llvm_unreachable("Unexpected tuple type in Clang type generation!");
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitProtocolType(CanProtocolType type) {
  if (auto lookupTy = getNamedSwiftType(type->getDecl()->getDeclContext(),
                                        "DynamicLookup"))
    if (lookupTy->isEqual(type)) {
      auto const &clangCtx = getClangASTContext();
      clang::QualType QT =
        clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, 0, 0);
      QT = clangCtx.getObjCObjectPointerType(QT);
      return clangCtx.getCanonicalType(QT);
    }
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) {
  auto const &clangCtx = getClangASTContext();
  clang::QualType clangType =
    clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinClassTy, 0, 0);
  clangType = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(clangType);
}

clang::CanQualType GenClangType::visitClassType(CanClassType type) {
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitBoundGenericStructType(
  CanBoundGenericStructType type) {
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitEnumType(CanEnumType type) {
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitFunctionType(CanFunctionType type) {
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitProtocolCompositionType(
  CanProtocolCompositionType type) {
  // It turns out that clang's encoding ignores the property list,
  // and essentially, does an encoding for 'id' type.
  // TODO. I am not sure if this covers all such encodings coming from
  // this swift type.
  auto const &clangCtx = getClangASTContext();
  clang::QualType QT =
    clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, 0, 0);
  QT = clangCtx.getObjCObjectPointerType(QT);
  return clangCtx.getCanonicalType(QT);
}

clang::CanQualType GenClangType::visitType(CanType type) {
  llvm_unreachable("Unhandled type in Clang type generation.");

  return clang::CanQualType();
}

const clang::ASTContext &GenClangType::getClangASTContext() const {
  auto *CI = static_cast<ClangImporter*>(&*Context.getClangModuleLoader());
  return CI->getClangASTContext();
}
<commit_msg>Fixes a comment, minor refactoring.<commit_after>//===--- GenClangType.cpp - Swift IR Generation For Types -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements generation of Clang AST types from Swift AST types
//  for types that are representable in Objective-C interfaces.
//
//===----------------------------------------------------------------------===//

#include "GenClangType.h"

#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/Type.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"

using namespace swift;
using namespace irgen;

static Type getNamedSwiftType(DeclContext *DC, StringRef name) {
  assert(DC && "Unexpected null declaration context!");

  auto &astContext = DC->getASTContext();
  UnqualifiedLookup lookup(astContext.getIdentifier(name), DC, nullptr);
  if (auto type = lookup.getSingleTypeResult())
    return type->getDeclaredType();

  return Type();
}

static const clang::CanQualType getClangBuiltinTypeFromKind(
  const clang::ASTContext &context,
  clang::BuiltinType::Kind kind) {
  switch (kind) {
#define BUILTIN_TYPE(Id, SingletonId) \
  case clang::BuiltinType::Id: return context.SingletonId;
#include "clang/AST/BuiltinTypes.def"
  }
}

static clang::CanQualType getClangSelectorType(
  const clang::ASTContext &clangCtx) {
  return clangCtx.getPointerType(clangCtx.ObjCBuiltinSelTy);
}

static clang::CanQualType getClangIdType(
  const clang::ASTContext &clangCtx) {
  clang::QualType clangType =
    clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, 0, 0);
  clangType = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(clangType);
}

clang::CanQualType GenClangType::visitStructType(CanStructType type) {
  // First attempt a lookup in our map of imported structs.
  auto *decl = type->getDecl();
  if (auto *clangDecl = decl->getClangDecl()) {
    auto *typeDecl = cast<clang::TypeDecl>(clangDecl);
    return typeDecl->getTypeForDecl()->getCanonicalTypeUnqualified();
  }

  auto const &clangCtx = getClangASTContext();

#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) {                \
    auto lookupTy = getNamedSwiftType(decl->getDeclContext(),                  \
                                      #SWIFT_TYPE_NAME);                       \
    if (lookupTy && lookupTy->isEqual(type))                                   \
      return getClangBuiltinTypeFromKind(clangCtx,                             \
                                      clang::BuiltinType::CLANG_BUILTIN_KIND); \
  }
#include "swift/ClangImporter/BuiltinMappedTypes.def"
#undef MAP_BUILTIN_TYPE

  // Handle other imported types.

#define CHECK_CLANG_TYPE_MATCH(TYPE, NAME, RESULT)                           \
  if (auto lookupTy = getNamedSwiftType((TYPE)->getDecl()->getDeclContext(), \
                                        (NAME)))                             \
    if (lookupTy->isEqual(type))                                             \
      return (RESULT);

  CHECK_CLANG_TYPE_MATCH(type, "COpaquePointer", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "Bool", clangCtx.SignedCharTy);
  CHECK_CLANG_TYPE_MATCH(type, "ObjCBool", clangCtx.ObjCBuiltinBoolTy);
  // FIXME: This is sufficient for ABI type generation, but should
  //        probably be const char* for type encoding.
  CHECK_CLANG_TYPE_MATCH(type, "CString", clangCtx.VoidPtrTy);
  CHECK_CLANG_TYPE_MATCH(type, "Selector", getClangSelectorType(clangCtx));
#undef CHECK_CLANG_TYPE_MATCH
  // FIXME: Handle other structs resulting from imported non-struct Clang types.
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitTupleType(CanTupleType type) {
  if (type->getNumElements() == 0)
    return getClangASTContext().VoidTy;

  llvm_unreachable("Unexpected tuple type in Clang type generation!");
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitProtocolType(CanProtocolType type) {
  if (auto lookupTy = getNamedSwiftType(type->getDecl()->getDeclContext(),
                                        "DynamicLookup"))
    if (lookupTy->isEqual(type))
      return getClangIdType(getClangASTContext());
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) {
  auto const &clangCtx = getClangASTContext();
  clang::QualType clangType =
    clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinClassTy, 0, 0);
  clangType = clangCtx.getObjCObjectPointerType(clangType);
  return clangCtx.getCanonicalType(clangType);
}

clang::CanQualType GenClangType::visitClassType(CanClassType type) {
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitBoundGenericStructType(
  CanBoundGenericStructType type) {
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitEnumType(CanEnumType type) {
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitFunctionType(CanFunctionType type) {
  return clang::CanQualType();
}

clang::CanQualType GenClangType::visitProtocolCompositionType(
  CanProtocolCompositionType type) {
  // Any protocol composition type in Swift that shows up in an @objc method maps 1-1 to
  // "id <SomeProto>"; with clang's encoding ignoring the protocol list.
  return getClangIdType(getClangASTContext());
}

clang::CanQualType GenClangType::visitType(CanType type) {
  llvm_unreachable("Unhandled type in Clang type generation.");

  return clang::CanQualType();
}

const clang::ASTContext &GenClangType::getClangASTContext() const {
  auto *CI = static_cast<ClangImporter*>(&*Context.getClangModuleLoader());
  return CI->getClangASTContext();
}
<|endoftext|>


<commit_before>/* Copyright (C) 2000-2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/*
  UNION  of select's
  UNION's  were introduced by Monty and Sinisa <sinisa@mysql.com>
*/

#include "mysql_priv.h"
#include "sql_select.h"

int mysql_union(THD *thd, LEX *lex, select_result *result,
		SELECT_LEX_UNIT *unit)
{
  DBUG_ENTER("mysql_union");
  int res= 0;
  if (!(res= unit->prepare(thd, result)))
    res= unit->exec();
  res|= unit->cleanup();
  DBUG_RETURN(res);
}

/***************************************************************************
** store records in temporary table for UNION
***************************************************************************/

select_union::select_union(TABLE *table_par)
  :table(table_par), not_describe(0)
{
  bzero((char*) &info,sizeof(info));
  /*
    We can always use DUP_IGNORE because the temporary table will only
    contain a unique key if we are using not using UNION ALL
  */
  info.handle_duplicates= DUP_IGNORE;
}

select_union::~select_union()
{
}

int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
  unit= u;
  return 0;
}

bool select_union::send_data(List<Item> &values)
{
  if (unit->offset_limit_cnt)
  {						// using limit offset,count
    unit->offset_limit_cnt--;
    return 0;
  }
  fill_record(table->field, values, 1);
  if (thd->net.report_error || write_record(table,&info))
  {
    if (thd->net.last_errno == ER_RECORD_FILE_FULL)
    {
      thd->clear_error(); // do not report user about table overflow
      if (create_myisam_from_heap(thd, table, &tmp_table_param,
				  info.last_errno, 1))
	return 1;
    }
    else
      return 1;
  }
  return 0;
}

bool select_union::send_eof()
{
  return 0;
}

bool select_union::flush()
{
  int error;
  if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
  {
    table->file->print_error(error,MYF(0));
    ::send_error(thd);
    return 1;
  }
  return 0;
}

int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result)
{
  SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
  SELECT_LEX *sl, *first_select;
  select_result *tmp_result;
  DBUG_ENTER("st_select_lex_unit::prepare");

  /*
    result object should be reassigned even if preparing already done for
    max/min subquery (ALL/ANY optimization)
  */
  result= sel_result;

  if (prepared)
    DBUG_RETURN(0);
  prepared= 1;
  res= 0;

  thd_arg->lex->current_select= sl= first_select= first_select_in_union();
  found_rows_for_union= first_select->options & OPTION_FOUND_ROWS;

  /* Global option */

  if (first_select->next_select())
  {
    if (!(tmp_result= union_result= new select_union(0)))
      goto err;
    union_result->not_describe= 1;
    union_result->tmp_table_param.init();
  }
  else
  {
    tmp_result= sel_result;
    // single select should be processed like select in p[arantses
    first_select->braces= 1;
  }

  for (sl= select_cursor; sl; sl= sl->next_select())
  {
    JOIN *join= new JOIN(thd_arg, sl->item_list,
			 sl->options | thd_arg->options | SELECT_NO_UNLOCK,
			 tmp_result);
    thd_arg->lex->current_select= sl;
    offset_limit_cnt= sl->offset_limit;
    select_limit_cnt= sl->select_limit+sl->offset_limit;
    if (select_limit_cnt < sl->select_limit)
      select_limit_cnt= HA_POS_ERROR;		// no limit
    if (select_limit_cnt == HA_POS_ERROR || sl->braces)
      sl->options&= ~OPTION_FOUND_ROWS;

    res= join->prepare(&sl->ref_pointer_array,
		       (TABLE_LIST*) sl->table_list.first, sl->with_wild,
		       sl->where,
		       ((sl->braces) ? sl->order_list.elements : 0) +
		       sl->group_list.elements,
		       (sl->braces) ?
		       (ORDER *)sl->order_list.first : (ORDER *) 0,
		       (ORDER*) sl->group_list.first,
		       sl->having,
		       (ORDER*) NULL,
		       sl, this);
    if (res || thd_arg->is_fatal_error)
      goto err;
    if (sl == first_select)
    {
      types.empty();
      List_iterator_fast<Item> it(sl->item_list);
      Item *item_tmp;
      while ((item_tmp= it++))
      {
	types.push_back(new Item_type_holder(thd_arg, item_tmp));
      }

      if (thd_arg->is_fatal_error)
	goto err; // out of memory
    }
    else
    {
      if (types.elements != sl->item_list.elements)
      {
	my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
		   ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
	goto err;
      }
      List_iterator_fast<Item> it(sl->item_list);
      List_iterator_fast<Item> tp(types);
      Item *type, *item_tmp;
      while ((type= tp++, item_tmp= it++))
      {
	if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
	  DBUG_RETURN(-1);
      }
    }
  }

  if (first_select->next_select())
  {
    union_result->tmp_table_param.field_count= types.elements;
    if (!(table= create_tmp_table(thd_arg,
				  &union_result->tmp_table_param, types,
				  (ORDER*) 0, !union_option, 1,
				  (first_select_in_union()->options |
				   thd_arg->options |
				   TMP_TABLE_ALL_COLUMNS),
				  HA_POS_ERROR, (char*) "")))
      goto err;
    table->file->extra(HA_EXTRA_WRITE_CACHE);
    table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
    bzero((char*) &result_table_list, sizeof(result_table_list));
    result_table_list.db= (char*) "";
    result_table_list.real_name= result_table_list.alias= (char*) "union";
    result_table_list.table= table;
    union_result->set_table(table);

    item_list.empty();
    thd_arg->lex->current_select= lex_select_save;
    {
      Field **field;
      for (field= table->field; *field; field++)
      {
	if (item_list.push_back(new Item_field(*field)))
	  DBUG_RETURN(-1);
      }
    }
  }
  else
    first_select->braces= 0; // remove our changes

  thd_arg->lex->current_select= lex_select_save;

  DBUG_RETURN(res || thd_arg->is_fatal_error ? 1 : 0);

err:
  thd_arg->lex->current_select= lex_select_save;
  DBUG_RETURN(-1);
}

int st_select_lex_unit::exec()
{
  SELECT_LEX *lex_select_save= thd->lex->current_select;
  SELECT_LEX *select_cursor=first_select_in_union();
  ulonglong add_rows=0;
  DBUG_ENTER("st_select_lex_unit::exec");

  if (executed && !uncacheable)
    DBUG_RETURN(0);
  executed= 1;

  if (uncacheable || !item || !item->assigned())
  {
    if (optimized && item && item->assigned())
    {
      item->assigned(0); // We will reinit & rexecute unit
      item->reset();
      table->file->delete_all_rows();
    }
    for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
    {
      ha_rows records_at_start= 0;
      thd->lex->current_select= sl;

      if (optimized)
	res= sl->join->reinit();
      else
      {
	if (sl != global_parameters)
	{
	  offset_limit_cnt= sl->offset_limit;
	  select_limit_cnt= sl->select_limit+sl->offset_limit;
	}
	else
	{
	  offset_limit_cnt= 0;
	  /*
	    We can't use LIMIT at this stage if we are using ORDER BY for the
	    whole query
	  */
	  if (sl->order_list.first)
	    select_limit_cnt= HA_POS_ERROR;
	  else
	    select_limit_cnt= sl->select_limit+sl->offset_limit;
	}
	if (select_limit_cnt < sl->select_limit)
	  select_limit_cnt= HA_POS_ERROR;		// no limit

	/*
	  When using braces, SQL_CALC_FOUND_ROWS affects the whole query.
	  We don't calculate found_rows() per union part
	*/
	if (select_limit_cnt == HA_POS_ERROR || sl->braces)
	  sl->options&= ~OPTION_FOUND_ROWS;
	else
	{
	  /*
	    We are doing an union without braces.  In this case
	    SQL_CALC_FOUND_ROWS should be done on all sub parts
	  */
	  sl->options|= found_rows_for_union;
	}
	sl->join->select_options=sl->options;
	/*
	  As far as union share table space we should reassign table map,
	  which can be spoiled by 'prepare' of JOIN of other UNION parts
	  if it use same tables
	*/
	uint tablenr=0;
	for (TABLE_LIST *table_list= (TABLE_LIST*) sl->table_list.first;
	     table_list;
	     table_list= table_list->next, tablenr++)
	{
	  if (table_list->shared)
	  {
	    /*
	      review notes: Check it carefully. I still can't understand
	      why I should not touch table->used_keys. For my point of
	      view we should do here same procedura as it was done by
	      setup_table
	    */
	    setup_table_map(table_list->table, table_list, tablenr);
	  }
	}
	res= sl->join->optimize();
      }
      if (!res)
      {
	records_at_start= table->file->records;
	sl->join->exec();
	res= sl->join->error;
	offset_limit_cnt= sl->offset_limit;
	if (!res && union_result->flush())
	{
	  thd->lex->current_select= lex_select_save;
	  DBUG_RETURN(1);
	}
      }
      if (res)
      {
	thd->lex->current_select= lex_select_save;
	DBUG_RETURN(res);
      }
      /* Needed for the following test and for records_at_start in next loop */
      table->file->info(HA_STATUS_VARIABLE);
      if (found_rows_for_union & sl->options)
      {
	/*
	  This is a union without braces. Remember the number of rows that
	  could also have been part of the result set.
	  We get this from the difference of between total number of possible
	  rows and actual rows added to the temporary table.
	*/
	add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong)
			      ((table->file->records -  records_at_start)));
      }
    }
  }
  optimized= 1;

  /* Send result to 'result' */

  res= -1;
  {
    List<Item_func_match> empty_list;
    empty_list.empty();

    if (!thd->is_fatal_error)				// Check if EOM
    {
      ulong options_tmp= thd->options;
      thd->lex->current_select= fake_select_lex;
      offset_limit_cnt= global_parameters->offset_limit;
      select_limit_cnt= global_parameters->select_limit +
	global_parameters->offset_limit;

      if (select_limit_cnt < global_parameters->select_limit)
	select_limit_cnt= HA_POS_ERROR;		// no limit
      if (select_limit_cnt == HA_POS_ERROR)
	options_tmp&= ~OPTION_FOUND_ROWS;
      else if (found_rows_for_union && !thd->lex->describe)
	options_tmp|= OPTION_FOUND_ROWS;
      fake_select_lex->ftfunc_list= &empty_list;
      fake_select_lex->table_list.link_in_list((byte *)&result_table_list,
					       (byte **)
					       &result_table_list.next);
      JOIN *join= fake_select_lex->join;
      if (!join)
      {
	/*
	  allocate JOIN for fake select only once (privent
	  mysql_select automatic allocation)
	*/
	fake_select_lex->join= new JOIN(thd, item_list, thd->options, result);
	/*
	  Fake st_select_lex should have item list for correctref_array
	  allocation.
	*/
	fake_select_lex->item_list= item_list;
      }
      else
      {
	JOIN_TAB *tab,*end;
	for (tab=join->join_tab,end=tab+join->tables ; tab != end ; tab++)
	{
	  delete tab->select;
	  delete tab->quick;
	}
	join->init(thd, item_list, thd->options, result);
      }
      res= mysql_select(thd, &fake_select_lex->ref_pointer_array,
			&result_table_list,
			0, item_list, NULL,
			global_parameters->order_list.elements,
			(ORDER*)global_parameters->order_list.first,
			(ORDER*) NULL, NULL, (ORDER*) NULL,
			options_tmp | SELECT_NO_UNLOCK,
			result, this, fake_select_lex);
      if (!res)
	thd->limit_found_rows = (ulonglong)table->file->records + add_rows;
      /*
	Mark for slow query log if any of the union parts didn't use
	indexes efficiently
      */
    }
  }
  thd->lex->current_select= lex_select_save;
  DBUG_RETURN(res);
}

int st_select_lex_unit::cleanup()
{
  int error= 0;
  DBUG_ENTER("st_select_lex_unit::cleanup");

  if (union_result)
  {
    delete union_result;
    if (table)
      free_tmp_table(thd, table);
    table= 0; // Safety
  }
  JOIN *join;
  for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select())
  {
    if ((join= sl->join))
    {
      error|= sl->join->cleanup();
      delete join;
    }
  }
  if (fake_select_lex && (join= fake_select_lex->join))
  {
    join->tables_list= 0;
    join->tables= 0;
    error|= join->cleanup();
    delete join;
  }
  DBUG_RETURN(error);
}
<commit_msg>Post-merge fixes<commit_after>/* Copyright (C) 2000-2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/*
  UNION  of select's
  UNION's  were introduced by Monty and Sinisa <sinisa@mysql.com>
*/

#include "mysql_priv.h"
#include "sql_select.h"

int mysql_union(THD *thd, LEX *lex, select_result *result,
		SELECT_LEX_UNIT *unit)
{
  DBUG_ENTER("mysql_union");
  int res= 0;
  if (!(res= unit->prepare(thd, result)))
    res= unit->exec();
  res|= unit->cleanup();
  DBUG_RETURN(res);
}

/***************************************************************************
** store records in temporary table for UNION
***************************************************************************/

select_union::select_union(TABLE *table_par)
  :table(table_par), not_describe(0)
{
  bzero((char*) &info,sizeof(info));
  /*
    We can always use DUP_IGNORE because the temporary table will only
    contain a unique key if we are using not using UNION ALL
  */
  info.handle_duplicates= DUP_IGNORE;
}

select_union::~select_union()
{
}

int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
  unit= u;
  return 0;
}

bool select_union::send_data(List<Item> &values)
{
  if (unit->offset_limit_cnt)
  {						// using limit offset,count
    unit->offset_limit_cnt--;
    return 0;
  }
  fill_record(table->field, values, 1);
  if (thd->net.report_error || write_record(table,&info))
  {
    if (thd->net.last_errno == ER_RECORD_FILE_FULL)
    {
      thd->clear_error(); // do not report user about table overflow
      if (create_myisam_from_heap(thd, table, &tmp_table_param,
				  info.last_errno, 1))
	return 1;
    }
    else
      return 1;
  }
  return 0;
}

bool select_union::send_eof()
{
  return 0;
}

bool select_union::flush()
{
  int error;
  if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
  {
    table->file->print_error(error,MYF(0));
    ::send_error(thd);
    return 1;
  }
  return 0;
}

int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result)
{
  SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
  SELECT_LEX *sl, *first_select;
  select_result *tmp_result;
  DBUG_ENTER("st_select_lex_unit::prepare");

  /*
    result object should be reassigned even if preparing already done for
    max/min subquery (ALL/ANY optimization)
  */
  result= sel_result;

  if (prepared)
    DBUG_RETURN(0);
  prepared= 1;
  res= 0;

  thd_arg->lex->current_select= sl= first_select= first_select_in_union();
  found_rows_for_union= first_select->options & OPTION_FOUND_ROWS;

  /* Global option */

  if (first_select->next_select())
  {
    if (!(tmp_result= union_result= new select_union(0)))
      goto err;
    union_result->not_describe= 1;
    union_result->tmp_table_param.init();
  }
  else
  {
    tmp_result= sel_result;
    // single select should be processed like select in p[arantses
    first_select->braces= 1;
  }

  for (;sl; sl= sl->next_select())
  {
    JOIN *join= new JOIN(thd_arg, sl->item_list,
			 sl->options | thd_arg->options | SELECT_NO_UNLOCK,
			 tmp_result);
    thd_arg->lex->current_select= sl;
    offset_limit_cnt= sl->offset_limit;
    select_limit_cnt= sl->select_limit+sl->offset_limit;
    if (select_limit_cnt < sl->select_limit)
      select_limit_cnt= HA_POS_ERROR;		// no limit
    if (select_limit_cnt == HA_POS_ERROR || sl->braces)
      sl->options&= ~OPTION_FOUND_ROWS;

    res= join->prepare(&sl->ref_pointer_array,
		       (TABLE_LIST*) sl->table_list.first, sl->with_wild,
		       sl->where,
		       ((sl->braces) ? sl->order_list.elements : 0) +
		       sl->group_list.elements,
		       (sl->braces) ?
		       (ORDER *)sl->order_list.first : (ORDER *) 0,
		       (ORDER*) sl->group_list.first,
		       sl->having,
		       (ORDER*) NULL,
		       sl, this);
    if (res || thd_arg->is_fatal_error)
      goto err;
    if (sl == first_select)
    {
      types.empty();
      List_iterator_fast<Item> it(sl->item_list);
      Item *item_tmp;
      while ((item_tmp= it++))
      {
	types.push_back(new Item_type_holder(thd_arg, item_tmp));
      }

      if (thd_arg->is_fatal_error)
	goto err; // out of memory
    }
    else
    {
      if (types.elements != sl->item_list.elements)
      {
	my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
		   ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
	goto err;
      }
      List_iterator_fast<Item> it(sl->item_list);
      List_iterator_fast<Item> tp(types);
      Item *type, *item_tmp;
      while ((type= tp++, item_tmp= it++))
      {
	if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
	  DBUG_RETURN(-1);
      }
    }
  }

  if (first_select->next_select())
  {
    union_result->tmp_table_param.field_count= types.elements;
    if (!(table= create_tmp_table(thd_arg,
				  &union_result->tmp_table_param, types,
				  (ORDER*) 0, !union_option, 1,
				  (first_select_in_union()->options |
				   thd_arg->options |
				   TMP_TABLE_ALL_COLUMNS),
				  HA_POS_ERROR, (char*) "")))
      goto err;
    table->file->extra(HA_EXTRA_WRITE_CACHE);
    table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
    bzero((char*) &result_table_list, sizeof(result_table_list));
    result_table_list.db= (char*) "";
    result_table_list.real_name= result_table_list.alias= (char*) "union";
    result_table_list.table= table;
    union_result->set_table(table);

    item_list.empty();
    thd_arg->lex->current_select= lex_select_save;
    {
      Field **field;
      for (field= table->field; *field; field++)
      {
	if (item_list.push_back(new Item_field(*field)))
	  DBUG_RETURN(-1);
      }
    }
  }
  else
    first_select->braces= 0; // remove our changes

  thd_arg->lex->current_select= lex_select_save;

  DBUG_RETURN(res || thd_arg->is_fatal_error ? 1 : 0);

err:
  thd_arg->lex->current_select= lex_select_save;
  DBUG_RETURN(-1);
}

int st_select_lex_unit::exec()
{
  SELECT_LEX *lex_select_save= thd->lex->current_select;
  SELECT_LEX *select_cursor=first_select_in_union();
  ulonglong add_rows=0;
  DBUG_ENTER("st_select_lex_unit::exec");

  if (executed && !uncacheable)
    DBUG_RETURN(0);
  executed= 1;

  if (uncacheable || !item || !item->assigned())
  {
    if (optimized && item && item->assigned())
    {
      item->assigned(0); // We will reinit & rexecute unit
      item->reset();
      table->file->delete_all_rows();
    }
    for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
    {
      ha_rows records_at_start= 0;
      thd->lex->current_select= sl;

      if (optimized)
	res= sl->join->reinit();
      else
      {
	if (sl != global_parameters)
	{
	  offset_limit_cnt= sl->offset_limit;
	  select_limit_cnt= sl->select_limit+sl->offset_limit;
	}
	else
	{
	  offset_limit_cnt= 0;
	  /*
	    We can't use LIMIT at this stage if we are using ORDER BY for the
	    whole query
	  */
	  if (sl->order_list.first)
	    select_limit_cnt= HA_POS_ERROR;
	  else
	    select_limit_cnt= sl->select_limit+sl->offset_limit;
	}
	if (select_limit_cnt < sl->select_limit)
	  select_limit_cnt= HA_POS_ERROR;		// no limit

	/*
	  When using braces, SQL_CALC_FOUND_ROWS affects the whole query.
	  We don't calculate found_rows() per union part
	*/
	if (select_limit_cnt == HA_POS_ERROR || sl->braces)
	  sl->options&= ~OPTION_FOUND_ROWS;
	else
	{
	  /*
	    We are doing an union without braces.  In this case
	    SQL_CALC_FOUND_ROWS should be done on all sub parts
	  */
	  sl->options|= found_rows_for_union;
	}
	sl->join->select_options=sl->options;
	/*
	  As far as union share table space we should reassign table map,
	  which can be spoiled by 'prepare' of JOIN of other UNION parts
	  if it use same tables
	*/
	uint tablenr=0;
	for (TABLE_LIST *table_list= (TABLE_LIST*) sl->table_list.first;
	     table_list;
	     table_list= table_list->next, tablenr++)
	{
	  if (table_list->shared)
	  {
	    /*
	      review notes: Check it carefully. I still can't understand
	      why I should not touch table->used_keys. For my point of
	      view we should do here same procedura as it was done by
	      setup_table
	    */
	    setup_table_map(table_list->table, table_list, tablenr);
	  }
	}
	res= sl->join->optimize();
      }
      if (!res)
      {
	records_at_start= table->file->records;
	sl->join->exec();
	res= sl->join->error;
	offset_limit_cnt= sl->offset_limit;
	if (!res && union_result->flush())
	{
	  thd->lex->current_select= lex_select_save;
	  DBUG_RETURN(1);
	}
      }
      if (res)
      {
	thd->lex->current_select= lex_select_save;
	DBUG_RETURN(res);
      }
      /* Needed for the following test and for records_at_start in next loop */
      table->file->info(HA_STATUS_VARIABLE);
      if (found_rows_for_union & sl->options)
      {
	/*
	  This is a union without braces. Remember the number of rows that
	  could also have been part of the result set.
	  We get this from the difference of between total number of possible
	  rows and actual rows added to the temporary table.
	*/
	add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong)
			      ((table->file->records -  records_at_start)));
      }
    }
  }
  optimized= 1;

  /* Send result to 'result' */

  res= -1;
  {
    List<Item_func_match> empty_list;
    empty_list.empty();

    if (!thd->is_fatal_error)				// Check if EOM
    {
      ulong options_tmp= thd->options;
      thd->lex->current_select= fake_select_lex;
      offset_limit_cnt= global_parameters->offset_limit;
      select_limit_cnt= global_parameters->select_limit +
	global_parameters->offset_limit;

      if (select_limit_cnt < global_parameters->select_limit)
	select_limit_cnt= HA_POS_ERROR;		// no limit
      if (select_limit_cnt == HA_POS_ERROR)
	options_tmp&= ~OPTION_FOUND_ROWS;
      else if (found_rows_for_union && !thd->lex->describe)
	options_tmp|= OPTION_FOUND_ROWS;
      fake_select_lex->ftfunc_list= &empty_list;
      fake_select_lex->table_list.link_in_list((byte *)&result_table_list,
					       (byte **)
					       &result_table_list.next);
      JOIN *join= fake_select_lex->join;
      if (!join)
      {
	/*
	  allocate JOIN for fake select only once (privent
	  mysql_select automatic allocation)
	*/
	fake_select_lex->join= new JOIN(thd, item_list, thd->options, result);
	/*
	  Fake st_select_lex should have item list for correctref_array
	  allocation.
	*/
	fake_select_lex->item_list= item_list;
      }
      else
      {
	JOIN_TAB *tab,*end;
	for (tab=join->join_tab,end=tab+join->tables ; tab != end ; tab++)
	{
	  delete tab->select;
	  delete tab->quick;
	}
	join->init(thd, item_list, thd->options, result);
      }
      res= mysql_select(thd, &fake_select_lex->ref_pointer_array,
			&result_table_list,
			0, item_list, NULL,
			global_parameters->order_list.elements,
			(ORDER*)global_parameters->order_list.first,
			(ORDER*) NULL, NULL, (ORDER*) NULL,
			options_tmp | SELECT_NO_UNLOCK,
			result, this, fake_select_lex);
      if (!res)
	thd->limit_found_rows = (ulonglong)table->file->records + add_rows;
      /*
	Mark for slow query log if any of the union parts didn't use
	indexes efficiently
      */
    }
  }
  thd->lex->current_select= lex_select_save;
  DBUG_RETURN(res);
}

int st_select_lex_unit::cleanup()
{
  int error= 0;
  DBUG_ENTER("st_select_lex_unit::cleanup");

  if (union_result)
  {
    delete union_result;
    if (table)
      free_tmp_table(thd, table);
    table= 0; // Safety
  }
  JOIN *join;
  for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select())
  {
    if ((join= sl->join))
    {
      error|= sl->join->cleanup();
      delete join;
    }
  }
  if (fake_select_lex && (join= fake_select_lex->join))
  {
    join->tables_list= 0;
    join->tables= 0;
    error|= join->cleanup();
    delete join;
  }
  DBUG_RETURN(error);
}
<|endoftext|>


<commit_before>#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <fstream>
#include <sstream>
#include <time.h>
#include <map>

#include <libwebsockets.h>

#define MAX_MESSAGE_QUEUE 32
#define MAX_RESPONSE 2047

static int close_testing;
int max_poll_elements;

static volatile int force_exit = 0;
static struct libwebsocket_context *context;

enum demo_protocols {
	/* always first */
	PROTOCOL_HTTP = 0,

	/* always last */
	DEMO_PROTOCOL_COUNT
};

const char *resource_path = "../..";

struct serveable {
	const char *urlpath;
	const char *mimetype;
};

struct per_session_data__http {
	int fd;
};

inline const char *getMimeType(const char *file)
{
	int n = strlen(file);
	if (n < 5)
		return 0;
	if (!strcmp(&file[n - 4], ".ico"))
		return "image/x-icon";
	if (!strcmp(&file[n - 4], ".png"))
		return "image/png";
	if (!strcmp(&file[n - 5], ".html"))
		return "text/html";
	if (!strcmp(&file[n - 4], ".css"))
		return "text/css";
	if (!strcmp(&file[n - 3], ".js"))
		return "text/javascript";
	return 0;
}

/* this protocol server (always the first one) just knows how to do HTTP */

static int callback_http(struct libwebsocket_context *context,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason, void *user,
							   void *in, size_t len)
{
	char buf[256];
	char leaf_path[1024];
	char b64[64];
	struct timeval tv;
	int n, m;
	unsigned char *p;
	char *other_headers;
	static unsigned char buffer[4096];
	struct stat stat_buf;
	struct per_session_data__http *pss =
			(struct per_session_data__http *)user;
	const char *mimetype;
#ifdef EXTERNAL_POLL
	struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
#endif
	unsigned char *end;
	switch (reason) {
	case LWS_CALLBACK_HTTP:
		if (len < 1) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_BAD_REQUEST, NULL);
			goto try_to_reuse;
		}

		/* this example server has no concept of directories */
		if (strchr((const char *)in + 1, '/')) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_FORBIDDEN, NULL);
			goto try_to_reuse;
		}

		/* if a legal POST URL, let it continue and accept data */
		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
			return 0;

		/* if not, send a file the easy way */
		strcpy(buf, resource_path);
		if (strcmp((char *)in, "/")) {
			if (*((const char *)in) != '/')
				strcat(buf, "/");
			strncat(buf, (char *)in, sizeof(buf) - strlen(resource_path));
		} else /* default file to serve */
			strcat(buf, "/index.html");
		buf[sizeof(buf) - 1] = '\0';
		printf("%s => %s\n", (char *)in, buf);

		/* refuse to serve files we don't understand */
		mimetype = getMimeType(buf);
		if (!mimetype) {
			lwsl_err("Unknown mimetype for %s\n", buf);
			libwebsockets_return_http_status(context, wsi,
				      HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
			return -1;
		}

		n = libwebsockets_serve_http_file(context, wsi, buf,
						mimetype, other_headers, n);
		if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
			return -1; /* error or can't reuse connection: close the socket */

		/*
		 * notice that the sending of the file completes asynchronously,
		 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
		 * it's done
		 */

		break;

	case LWS_CALLBACK_HTTP_BODY:
		strncpy(buf, (char *)in, 20);
		buf[20] = '\0';
		if (len < 20)
			buf[len] = '\0';

		lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
				(const char *)buf, (int)len);

		break;

	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
		lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
		/* the whole of the sent body arrived, close or reuse the connection */
		libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_OK, NULL);
		goto try_to_reuse;

	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
//		lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
		/* kill the connection after we sent one file */
		goto try_to_reuse;

	case LWS_CALLBACK_HTTP_WRITEABLE:
		/*
		 * we can send more of whatever it is we were sending
		 */
		do {
			/* we'd like the send this much */
			n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;

			/* but if the peer told us he wants less, we can adapt */
			m = lws_get_peer_write_allowance(wsi);

			/* -1 means not using a protocol that has this info */
			if (m == 0)
				/* right now, peer can't handle anything */
				goto later;

			if (m != -1 && m < n)
				/* he couldn't handle that much */
				n = m;

			n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
									n);
			/* problem reading, close conn */
			if (n < 0)
				goto bail;
			/* sent it all, close conn */
			if (n == 0)
				goto flush_bail;
			/*
			 * To support HTTP2, must take care about preamble space
			 *
			 * identification of when we send the last payload frame
			 * is handled by the library itself if you sent a
			 * content-length header
			 */
			m = libwebsocket_write(wsi,
					       buffer + LWS_SEND_BUFFER_PRE_PADDING,
					       n, LWS_WRITE_HTTP);
			if (m < 0)
				/* write failed, close conn */
				goto bail;

			/*
			 * http2 won't do this
			 */
			if (m != n)
				/* partial write, adjust */
				if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
					goto bail;

			if (m) /* while still active, extend timeout */
				libwebsocket_set_timeout(wsi,
					PENDING_TIMEOUT_HTTP_CONTENT, 5);

			/* if we have indigestion, let him clear it before eating more */
			if (lws_partial_buffered(wsi))
				break;

		} while (!lws_send_pipe_choked(wsi));

later:
		libwebsocket_callback_on_writable(context, wsi);
		break;
flush_bail:
		/* true if still partial pending */
		if (lws_partial_buffered(wsi)) {
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}
		close(pss->fd);
		goto try_to_reuse;

bail:
		close(pss->fd);
		return -1;

	/*
	 * callback for confirming to continue with client IP appear in
	 * protocol 0 callback since no websocket protocol has been agreed
	 * yet.  You can just ignore this if you won't filter on client IP
	 * since the default uhandled callback return is 0 meaning let the
	 * connection continue.
	 */

	case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:

		/* if we returned non-zero from here, we kill the connection */
		break;

	case LWS_CALLBACK_GET_THREAD_ID:
		/*
		 * if you will call "libwebsocket_callback_on_writable"
		 * from a different thread, return the caller thread ID
		 * here so lws can use this information to work out if it
		 * should signal the poll() loop to exit and restart early
		 */

		/* return pthread_getthreadid_np(); */

		break;

	default:
		break;
	}

	return 0;

try_to_reuse:
	if (lws_http_transaction_completed(wsi))
		return -1;

	return 0;
}

/* list of supported protocols and callbacks */

static struct libwebsocket_protocols protocols[] = {
	/* first protocol must always be HTTP handler */

	{
		"http-only",		/* name */
		callback_http,		/* callback */
		sizeof (struct per_session_data__http),	/* per_session_data_size */
		0,			/* max frame size / rx buffer */
	},
	{ NULL, NULL, 0, 0 } /* terminator */
};

void sighandler(int sig)
{
	force_exit = 1;
	libwebsocket_cancel_service(context);
}

int main(int argc, char **argv)
{
	int n = 0;
	int use_ssl = 0;
	int opts = 0;
	char interface_name[128] = "";
	const char *iface = NULL;
	struct lws_context_creation_info info;

	int debug_level = 7;

	memset(&info, 0, sizeof info);
	info.port = 8085;

	signal(SIGINT, sighandler);

	/* tell the library what debug level to emit and to send it to syslog */
	lws_set_log_level(0, NULL);

	printf("Using resource path \"%s\"\n", resource_path);

	info.iface = iface;
	info.protocols = protocols;

	info.ssl_cert_filepath = NULL;
	info.ssl_private_key_filepath = NULL;

	info.gid = -1;
	info.uid = -1;
	info.options = opts;

	context = libwebsocket_create_context(&info);
	if (context == NULL) {
		lwsl_err("libwebsocket init failed\n");
		return -1;
	}

	n = 0;
	while (n >= 0 && !force_exit) {
		struct timeval tv;

		gettimeofday(&tv, NULL);

		/*
		 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
		 * live websocket connection using the DUMB_INCREMENT protocol,
		 * as soon as it can take more packets (usually immediately)
		 */

		/*
		 * If libwebsockets sockets are all we care about,
		 * you can use this api which takes care of the poll()
		 * and looping through finding who needed service.
		 *
		 * If no socket needs service, it'll return anyway after
		 * the number of ms in the second argument.
		 */

		n = libwebsocket_service(context, 50);
	}

	libwebsocket_context_destroy(context);

	lwsl_notice("libwebsockets-test-server exited cleanly\n");

	return 0;
}
<commit_msg>Fixed segfault in local-web<commit_after>#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <fstream>
#include <sstream>
#include <time.h>
#include <map>

#include <libwebsockets.h>

#define MAX_MESSAGE_QUEUE 32
#define MAX_RESPONSE 2047

static int close_testing;
int max_poll_elements;

static volatile int force_exit = 0;
static struct libwebsocket_context *context;

enum demo_protocols {
	/* always first */
	PROTOCOL_HTTP = 0,

	/* always last */
	DEMO_PROTOCOL_COUNT
};

const char *resource_path = "../..";

struct serveable {
	const char *urlpath;
	const char *mimetype;
};

struct per_session_data__http {
	int fd;
};

inline const char *getMimeType(const char *file)
{
	int n = strlen(file);
	if (n < 5)
		return 0;
	if (!strcmp(&file[n - 4], ".ico"))
		return "image/x-icon";
	if (!strcmp(&file[n - 4], ".png"))
		return "image/png";
	if (!strcmp(&file[n - 5], ".html"))
		return "text/html";
	if (!strcmp(&file[n - 4], ".css"))
		return "text/css";
	if (!strcmp(&file[n - 3], ".js"))
		return "text/javascript";
	return 0;
}

/* this protocol server (always the first one) just knows how to do HTTP */

static int callback_http(struct libwebsocket_context *context,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason, void *user,
							   void *in, size_t len)
{
	char buf[256];
	char leaf_path[1024];
	char b64[64];
	struct timeval tv;
	int n, m;
	unsigned char *p;
	char *other_headers = 0;
	static unsigned char buffer[4096];
	struct stat stat_buf;
	struct per_session_data__http *pss =
			(struct per_session_data__http *)user;
	const char *mimetype;
#ifdef EXTERNAL_POLL
	struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
#endif
	unsigned char *end;
	switch (reason) {
	case LWS_CALLBACK_HTTP:
		if (len < 1) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_BAD_REQUEST, NULL);
			goto try_to_reuse;
		}

		/* this example server has no concept of directories */
		if (strchr((const char *)in + 1, '/')) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_FORBIDDEN, NULL);
			goto try_to_reuse;
		}

		/* if a legal POST URL, let it continue and accept data */
		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
			return 0;

		/* if not, send a file the easy way */
		strcpy(buf, resource_path);
		if (strcmp((char *)in, "/")) {
			if (*((const char *)in) != '/')
				strcat(buf, "/");
			strncat(buf, (char *)in, sizeof(buf) - strlen(resource_path));
		} else /* default file to serve */
			strcat(buf, "/index.html");
		buf[sizeof(buf) - 1] = '\0';
		printf("%s => %s\n", (char *)in, buf);

		/* refuse to serve files we don't understand */
		mimetype = getMimeType(buf);
		if (!mimetype) {
			lwsl_err("Unknown mimetype for %s\n", buf);
			libwebsockets_return_http_status(context, wsi,
				      HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
			return -1;
		}

		n = libwebsockets_serve_http_file(context, wsi, buf,
						mimetype, other_headers, n);
		if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
			return -1; /* error or can't reuse connection: close the socket */

		/*
		 * notice that the sending of the file completes asynchronously,
		 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
		 * it's done
		 */

		break;

	case LWS_CALLBACK_HTTP_BODY:
		strncpy(buf, (char *)in, 20);
		buf[20] = '\0';
		if (len < 20)
			buf[len] = '\0';

		lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
				(const char *)buf, (int)len);

		break;

	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
		lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
		/* the whole of the sent body arrived, close or reuse the connection */
		libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_OK, NULL);
		goto try_to_reuse;

	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
//		lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
		/* kill the connection after we sent one file */
		goto try_to_reuse;

	case LWS_CALLBACK_HTTP_WRITEABLE:
		/*
		 * we can send more of whatever it is we were sending
		 */
		do {
			/* we'd like the send this much */
			n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;

			/* but if the peer told us he wants less, we can adapt */
			m = lws_get_peer_write_allowance(wsi);

			/* -1 means not using a protocol that has this info */
			if (m == 0)
				/* right now, peer can't handle anything */
				goto later;

			if (m != -1 && m < n)
				/* he couldn't handle that much */
				n = m;

			n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
									n);
			/* problem reading, close conn */
			if (n < 0)
				goto bail;
			/* sent it all, close conn */
			if (n == 0)
				goto flush_bail;
			/*
			 * To support HTTP2, must take care about preamble space
			 *
			 * identification of when we send the last payload frame
			 * is handled by the library itself if you sent a
			 * content-length header
			 */
			m = libwebsocket_write(wsi,
					       buffer + LWS_SEND_BUFFER_PRE_PADDING,
					       n, LWS_WRITE_HTTP);
			if (m < 0)
				/* write failed, close conn */
				goto bail;

			/*
			 * http2 won't do this
			 */
			if (m != n)
				/* partial write, adjust */
				if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
					goto bail;

			if (m) /* while still active, extend timeout */
				libwebsocket_set_timeout(wsi,
					PENDING_TIMEOUT_HTTP_CONTENT, 5);

			/* if we have indigestion, let him clear it before eating more */
			if (lws_partial_buffered(wsi))
				break;

		} while (!lws_send_pipe_choked(wsi));

later:
		libwebsocket_callback_on_writable(context, wsi);
		break;
flush_bail:
		/* true if still partial pending */
		if (lws_partial_buffered(wsi)) {
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}
		close(pss->fd);
		goto try_to_reuse;

bail:
		close(pss->fd);
		return -1;

	/*
	 * callback for confirming to continue with client IP appear in
	 * protocol 0 callback since no websocket protocol has been agreed
	 * yet.  You can just ignore this if you won't filter on client IP
	 * since the default uhandled callback return is 0 meaning let the
	 * connection continue.
	 */

	case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:

		/* if we returned non-zero from here, we kill the connection */
		break;

	case LWS_CALLBACK_GET_THREAD_ID:
		/*
		 * if you will call "libwebsocket_callback_on_writable"
		 * from a different thread, return the caller thread ID
		 * here so lws can use this information to work out if it
		 * should signal the poll() loop to exit and restart early
		 */

		/* return pthread_getthreadid_np(); */

		break;

	default:
		break;
	}

	return 0;

try_to_reuse:
	if (lws_http_transaction_completed(wsi))
		return -1;

	return 0;
}

/* list of supported protocols and callbacks */

static struct libwebsocket_protocols protocols[] = {
	/* first protocol must always be HTTP handler */

	{
		"http-only",		/* name */
		callback_http,		/* callback */
		sizeof (struct per_session_data__http),	/* per_session_data_size */
		0,			/* max frame size / rx buffer */
	},
	{ NULL, NULL, 0, 0 } /* terminator */
};

void sighandler(int sig)
{
	force_exit = 1;
	libwebsocket_cancel_service(context);
}

int main(int argc, char **argv)
{
	int n = 0;
	int use_ssl = 0;
	int opts = 0;
	char interface_name[128] = "";
	const char *iface = NULL;
	struct lws_context_creation_info info;

	int debug_level = 7;

	memset(&info, 0, sizeof info);
	info.port = 8085;

	signal(SIGINT, sighandler);

	/* tell the library what debug level to emit and to send it to syslog */
	lws_set_log_level(0, NULL);

	printf("Using resource path \"%s\"\n", resource_path);

	info.iface = iface;
	info.protocols = protocols;

	info.ssl_cert_filepath = NULL;
	info.ssl_private_key_filepath = NULL;

	info.gid = -1;
	info.uid = -1;
	info.options = opts;

	context = libwebsocket_create_context(&info);
	if (context == NULL) {
		lwsl_err("libwebsocket init failed\n");
		return -1;
	}

	n = 0;
	while (n >= 0 && !force_exit) {
		struct timeval tv;

		gettimeofday(&tv, NULL);

		/*
		 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
		 * live websocket connection using the DUMB_INCREMENT protocol,
		 * as soon as it can take more packets (usually immediately)
		 */

		/*
		 * If libwebsockets sockets are all we care about,
		 * you can use this api which takes care of the poll()
		 * and looping through finding who needed service.
		 *
		 * If no socket needs service, it'll return anyway after
		 * the number of ms in the second argument.
		 */

		n = libwebsocket_service(context, 50);
	}

	libwebsocket_context_destroy(context);

	lwsl_notice("libwebsockets-test-server exited cleanly\n");

	return 0;
}
<|endoftext|>