add webgl support
This commit is contained in:
parent
046f573737
commit
281a4c920f
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,387 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef WEBGL_FORMATS_H_
|
||||
#define WEBGL_FORMATS_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "WebGLTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgl {
|
||||
|
||||
typedef uint8_t EffectiveFormatValueT;
|
||||
|
||||
enum class EffectiveFormat : EffectiveFormatValueT {
|
||||
// GLES 3.0.4, p128-129, "Required Texture Formats"
|
||||
// "Texture and renderbuffer color formats"
|
||||
RGBA32I,
|
||||
RGBA32UI,
|
||||
RGBA16I,
|
||||
RGBA16UI,
|
||||
RGBA8,
|
||||
RGBA8I,
|
||||
RGBA8UI,
|
||||
SRGB8_ALPHA8,
|
||||
RGB10_A2,
|
||||
RGB10_A2UI,
|
||||
RGBA4,
|
||||
RGB5_A1,
|
||||
|
||||
RGB8,
|
||||
RGB565,
|
||||
|
||||
RG32I,
|
||||
RG32UI,
|
||||
RG16I,
|
||||
RG16UI,
|
||||
RG8,
|
||||
RG8I,
|
||||
RG8UI,
|
||||
|
||||
R32I,
|
||||
R32UI,
|
||||
R16I,
|
||||
R16UI,
|
||||
R8,
|
||||
R8I,
|
||||
R8UI,
|
||||
|
||||
// "Texture-only color formats"
|
||||
RGBA32F,
|
||||
RGBA16F,
|
||||
RGBA8_SNORM,
|
||||
|
||||
RGB32F,
|
||||
RGB32I,
|
||||
RGB32UI,
|
||||
|
||||
RGB16F,
|
||||
RGB16I,
|
||||
RGB16UI,
|
||||
|
||||
RGB8_SNORM,
|
||||
RGB8I,
|
||||
RGB8UI,
|
||||
SRGB8,
|
||||
|
||||
R11F_G11F_B10F,
|
||||
RGB9_E5,
|
||||
|
||||
RG32F,
|
||||
RG16F,
|
||||
RG8_SNORM,
|
||||
|
||||
R32F,
|
||||
R16F,
|
||||
R8_SNORM,
|
||||
|
||||
// "Depth formats"
|
||||
DEPTH_COMPONENT32F,
|
||||
DEPTH_COMPONENT24,
|
||||
DEPTH_COMPONENT16,
|
||||
|
||||
// "Combined depth+stencil formats"
|
||||
DEPTH32F_STENCIL8,
|
||||
DEPTH24_STENCIL8,
|
||||
|
||||
// GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
|
||||
STENCIL_INDEX8,
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
// GLES 3.0.4, p147, table 3.19
|
||||
// GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
|
||||
COMPRESSED_R11_EAC,
|
||||
COMPRESSED_SIGNED_R11_EAC,
|
||||
COMPRESSED_RG11_EAC,
|
||||
COMPRESSED_SIGNED_RG11_EAC,
|
||||
COMPRESSED_RGB8_ETC2,
|
||||
COMPRESSED_SRGB8_ETC2,
|
||||
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
|
||||
COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
|
||||
COMPRESSED_RGBA8_ETC2_EAC,
|
||||
COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
|
||||
|
||||
// AMD_compressed_ATC_texture
|
||||
ATC_RGB_AMD,
|
||||
ATC_RGBA_EXPLICIT_ALPHA_AMD,
|
||||
ATC_RGBA_INTERPOLATED_ALPHA_AMD,
|
||||
|
||||
// EXT_texture_compression_s3tc
|
||||
COMPRESSED_RGB_S3TC_DXT1_EXT,
|
||||
COMPRESSED_RGBA_S3TC_DXT1_EXT,
|
||||
COMPRESSED_RGBA_S3TC_DXT3_EXT,
|
||||
COMPRESSED_RGBA_S3TC_DXT5_EXT,
|
||||
|
||||
// EXT_texture_sRGB
|
||||
COMPRESSED_SRGB_S3TC_DXT1_EXT,
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
|
||||
COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,
|
||||
|
||||
// KHR_texture_compression_astc_ldr
|
||||
COMPRESSED_RGBA_ASTC_4x4_KHR,
|
||||
COMPRESSED_RGBA_ASTC_5x4_KHR,
|
||||
COMPRESSED_RGBA_ASTC_5x5_KHR,
|
||||
COMPRESSED_RGBA_ASTC_6x5_KHR,
|
||||
COMPRESSED_RGBA_ASTC_6x6_KHR,
|
||||
COMPRESSED_RGBA_ASTC_8x5_KHR,
|
||||
COMPRESSED_RGBA_ASTC_8x6_KHR,
|
||||
COMPRESSED_RGBA_ASTC_8x8_KHR,
|
||||
COMPRESSED_RGBA_ASTC_10x5_KHR,
|
||||
COMPRESSED_RGBA_ASTC_10x6_KHR,
|
||||
COMPRESSED_RGBA_ASTC_10x8_KHR,
|
||||
COMPRESSED_RGBA_ASTC_10x10_KHR,
|
||||
COMPRESSED_RGBA_ASTC_12x10_KHR,
|
||||
COMPRESSED_RGBA_ASTC_12x12_KHR,
|
||||
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR,
|
||||
COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR,
|
||||
|
||||
// IMG_texture_compression_pvrtc
|
||||
COMPRESSED_RGB_PVRTC_4BPPV1,
|
||||
COMPRESSED_RGBA_PVRTC_4BPPV1,
|
||||
COMPRESSED_RGB_PVRTC_2BPPV1,
|
||||
COMPRESSED_RGBA_PVRTC_2BPPV1,
|
||||
|
||||
// OES_compressed_ETC1_RGB8_texture
|
||||
ETC1_RGB8_OES,
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
// GLES 3.0.4, p128, table 3.12.
|
||||
Luminance8Alpha8,
|
||||
Luminance8,
|
||||
Alpha8,
|
||||
|
||||
// OES_texture_float
|
||||
Luminance32FAlpha32F,
|
||||
Luminance32F,
|
||||
Alpha32F,
|
||||
|
||||
// OES_texture_half_float
|
||||
Luminance16FAlpha16F,
|
||||
Luminance16F,
|
||||
Alpha16F,
|
||||
|
||||
MAX,
|
||||
};
|
||||
|
||||
enum class UnsizedFormat : uint8_t {
|
||||
R,
|
||||
RG,
|
||||
RGB,
|
||||
RGBA,
|
||||
LA,
|
||||
L,
|
||||
A,
|
||||
D,
|
||||
S,
|
||||
DEPTH_STENCIL, // `DS` is a macro on Solaris. (regset.h)
|
||||
};
|
||||
|
||||
// GLES 3.0.4 p114 Table 3.4, p240
|
||||
enum class ComponentType : uint8_t {
|
||||
None,
|
||||
Int, // RGBA32I
|
||||
UInt, // RGBA32UI, STENCIL_INDEX8
|
||||
NormInt, // RGBA8_SNORM
|
||||
NormUInt, // RGBA8, DEPTH_COMPONENT16
|
||||
Float, // RGBA32F
|
||||
Special, // DEPTH24_STENCIL8
|
||||
};
|
||||
|
||||
enum class CompressionFamily : uint8_t {
|
||||
ASTC,
|
||||
ATC,
|
||||
ES3, // ETC2 or EAC
|
||||
ETC1,
|
||||
PVRTC,
|
||||
S3TC,
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct CompressedFormatInfo
|
||||
{
|
||||
const EffectiveFormat effectiveFormat;
|
||||
const uint8_t bytesPerBlock;
|
||||
const uint8_t blockWidth;
|
||||
const uint8_t blockHeight;
|
||||
const CompressionFamily family;
|
||||
};
|
||||
|
||||
struct FormatInfo
|
||||
{
|
||||
const EffectiveFormat effectiveFormat;
|
||||
const char* const name;
|
||||
const GLenum sizedFormat;
|
||||
const UnsizedFormat unsizedFormat;
|
||||
const ComponentType componentType;
|
||||
const bool isSRGB;
|
||||
|
||||
const CompressedFormatInfo* const compression;
|
||||
|
||||
const uint8_t estimatedBytesPerPixel; // 0 iff bool(compression).
|
||||
|
||||
// In bits. Iff bool(compression), active channels are 1.
|
||||
const uint8_t r;
|
||||
const uint8_t g;
|
||||
const uint8_t b;
|
||||
const uint8_t a;
|
||||
const uint8_t d;
|
||||
const uint8_t s;
|
||||
|
||||
//////
|
||||
|
||||
std::map<UnsizedFormat, const FormatInfo*> copyDecayFormats;
|
||||
|
||||
const FormatInfo* GetCopyDecayFormat(UnsizedFormat) const;
|
||||
|
||||
bool IsColorFormat() const {
|
||||
// Alpha is a 'color format' since it's 'color-attachable'.
|
||||
return bool(compression) ||
|
||||
bool(r | g | b | a);
|
||||
}
|
||||
};
|
||||
|
||||
struct PackingInfo
|
||||
{
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
|
||||
bool operator <(const PackingInfo& x) const
|
||||
{
|
||||
if (format != x.format)
|
||||
return format < x.format;
|
||||
|
||||
return type < x.type;
|
||||
}
|
||||
|
||||
bool operator ==(const PackingInfo& x) const {
|
||||
return (format == x.format &&
|
||||
type == x.type);
|
||||
}
|
||||
};
|
||||
|
||||
struct DriverUnpackInfo
|
||||
{
|
||||
GLenum internalFormat;
|
||||
GLenum unpackFormat;
|
||||
GLenum unpackType;
|
||||
|
||||
PackingInfo ToPacking() const {
|
||||
return {unpackFormat, unpackType};
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const FormatInfo* GetFormat(EffectiveFormat format);
|
||||
uint8_t BytesPerPixel(const PackingInfo& packing);
|
||||
bool GetBytesPerPixel(const PackingInfo& packing, uint8_t* const out_bytes);
|
||||
/*
|
||||
GLint ComponentSize(const FormatInfo* format, GLenum component);
|
||||
GLenum ComponentType(const FormatInfo* format);
|
||||
*/
|
||||
////////////////////////////////////////
|
||||
|
||||
struct FormatUsageInfo
|
||||
{
|
||||
const FormatInfo* const format;
|
||||
private:
|
||||
bool isRenderable;
|
||||
public:
|
||||
bool isFilterable;
|
||||
|
||||
std::map<PackingInfo, DriverUnpackInfo> validUnpacks;
|
||||
const DriverUnpackInfo* idealUnpack;
|
||||
|
||||
const GLint* textureSwizzleRGBA;
|
||||
|
||||
bool maxSamplesKnown;
|
||||
uint32_t maxSamples;
|
||||
|
||||
static const GLint kLuminanceSwizzleRGBA[4];
|
||||
static const GLint kAlphaSwizzleRGBA[4];
|
||||
static const GLint kLumAlphaSwizzleRGBA[4];
|
||||
|
||||
explicit FormatUsageInfo(const FormatInfo* _format)
|
||||
: format(_format)
|
||||
, isRenderable(false)
|
||||
, isFilterable(false)
|
||||
, idealUnpack(nullptr)
|
||||
, textureSwizzleRGBA(nullptr)
|
||||
, maxSamplesKnown(false)
|
||||
, maxSamples(0)
|
||||
{ }
|
||||
|
||||
bool IsRenderable() const { return isRenderable; }
|
||||
void SetRenderable();
|
||||
|
||||
bool IsUnpackValid(const PackingInfo& key,
|
||||
const DriverUnpackInfo** const out_value) const;
|
||||
|
||||
void ResolveMaxSamples(gl::GLContext* gl);
|
||||
};
|
||||
|
||||
class FormatUsageAuthority
|
||||
{
|
||||
std::map<EffectiveFormat, FormatUsageInfo> mUsageMap;
|
||||
|
||||
std::map<GLenum, const FormatUsageInfo*> mRBFormatMap;
|
||||
std::map<GLenum, const FormatUsageInfo*> mSizedTexFormatMap;
|
||||
std::map<PackingInfo, const FormatUsageInfo*> mUnsizedTexFormatMap;
|
||||
|
||||
std::set<GLenum> mValidTexInternalFormats;
|
||||
std::set<GLenum> mValidTexUnpackFormats;
|
||||
std::set<GLenum> mValidTexUnpackTypes;
|
||||
|
||||
public:
|
||||
static UniquePtr<FormatUsageAuthority> CreateForWebGL1(gl::GLContext* gl);
|
||||
static UniquePtr<FormatUsageAuthority> CreateForWebGL2(gl::GLContext* gl);
|
||||
|
||||
private:
|
||||
FormatUsageAuthority() { }
|
||||
|
||||
public:
|
||||
FormatUsageInfo* EditUsage(EffectiveFormat format);
|
||||
const FormatUsageInfo* GetUsage(EffectiveFormat format) const;
|
||||
|
||||
void AddTexUnpack(FormatUsageInfo* usage, const PackingInfo& pi,
|
||||
const DriverUnpackInfo& dui);
|
||||
|
||||
bool IsInternalFormatEnumValid(GLenum internalFormat) const;
|
||||
bool AreUnpackEnumsValid(GLenum unpackFormat, GLenum unpackType) const;
|
||||
|
||||
void AllowRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage);
|
||||
void AllowSizedTexFormat(GLenum sizedFormat, const FormatUsageInfo* usage);
|
||||
void AllowUnsizedTexFormat(const PackingInfo& pi, const FormatUsageInfo* usage);
|
||||
|
||||
const FormatUsageInfo* GetRBUsage(GLenum sizedFormat) const;
|
||||
const FormatUsageInfo* GetSizedTexUsage(GLenum sizedFormat) const;
|
||||
const FormatUsageInfo* GetUnsizedTexUsage(const PackingInfo& pi) const;
|
||||
};
|
||||
|
||||
} // namespace webgl
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // WEBGL_FORMATS_H_
|
||||
|
|
@ -0,0 +1,435 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//cjh #include "WebGLContext.h"
|
||||
#include "WebGLTexelConversions.h"
|
||||
#include <memory.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace WebGLTexelConversions;
|
||||
|
||||
namespace {
|
||||
|
||||
/** @class WebGLImageConverter
|
||||
*
|
||||
* This class is just a helper to implement WebGLContext::ConvertImage below.
|
||||
*
|
||||
* Design comments:
|
||||
*
|
||||
* WebGLContext::ConvertImage has to handle hundreds of format conversion paths.
|
||||
* It is important to minimize executable code size here. Instead of passing around
|
||||
* a large number of function parameters hundreds of times, we create a
|
||||
* WebGLImageConverter object once, storing these parameters, and then we call
|
||||
* the run() method on it.
|
||||
*/
|
||||
class WebGLImageConverter
|
||||
{
|
||||
const size_t mWidth, mHeight;
|
||||
const void* const mSrcStart;
|
||||
void* const mDstStart;
|
||||
const ptrdiff_t mSrcStride, mDstStride;
|
||||
bool mAlreadyRun;
|
||||
bool mSuccess;
|
||||
|
||||
/*
|
||||
* Returns sizeof(texel)/sizeof(type). The point is that we will iterate over
|
||||
* texels with typed pointers and this value will tell us by how much we need
|
||||
* to increment these pointers to advance to the next texel.
|
||||
*/
|
||||
template<WebGLTexelFormat Format>
|
||||
static size_t NumElementsPerTexelForFormat() {
|
||||
switch (Format) {
|
||||
case WebGLTexelFormat::A8:
|
||||
case WebGLTexelFormat::A16F:
|
||||
case WebGLTexelFormat::A32F:
|
||||
case WebGLTexelFormat::R8:
|
||||
case WebGLTexelFormat::R16F:
|
||||
case WebGLTexelFormat::R32F:
|
||||
case WebGLTexelFormat::RGB565:
|
||||
case WebGLTexelFormat::RGB11F11F10F:
|
||||
case WebGLTexelFormat::RGBA4444:
|
||||
case WebGLTexelFormat::RGBA5551:
|
||||
return 1;
|
||||
case WebGLTexelFormat::RA8:
|
||||
case WebGLTexelFormat::RA16F:
|
||||
case WebGLTexelFormat::RA32F:
|
||||
case WebGLTexelFormat::RG8:
|
||||
case WebGLTexelFormat::RG16F:
|
||||
case WebGLTexelFormat::RG32F:
|
||||
return 2;
|
||||
case WebGLTexelFormat::RGB8:
|
||||
case WebGLTexelFormat::RGB16F:
|
||||
case WebGLTexelFormat::RGB32F:
|
||||
return 3;
|
||||
case WebGLTexelFormat::RGBA8:
|
||||
case WebGLTexelFormat::RGBA16F:
|
||||
case WebGLTexelFormat::RGBA32F:
|
||||
case WebGLTexelFormat::BGRX8:
|
||||
case WebGLTexelFormat::BGRA8:
|
||||
return 4;
|
||||
default:
|
||||
MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the completely format-specific templatized conversion function,
|
||||
* that will be instantiated hundreds of times for all different combinations.
|
||||
* It is important to avoid generating useless code here. In particular, many
|
||||
* instantiations of this function template will never be called, so we try
|
||||
* to return immediately in these cases to allow the compiler to avoid generating
|
||||
* useless code.
|
||||
*/
|
||||
template<WebGLTexelFormat SrcFormat,
|
||||
WebGLTexelFormat DstFormat,
|
||||
WebGLTexelPremultiplicationOp PremultiplicationOp>
|
||||
void run()
|
||||
{
|
||||
// check for never-called cases. We early-return to allow the compiler
|
||||
// to avoid generating this code. It would be tempting to abort() instead,
|
||||
// as returning early does leave the destination surface with uninitialized
|
||||
// data, but that would not allow the compiler to avoid generating this code.
|
||||
// So instead, we return early, so Success() will return false, and the caller
|
||||
// must check that and abort in that case. See WebGLContext::ConvertImage.
|
||||
|
||||
if (SrcFormat == DstFormat &&
|
||||
PremultiplicationOp == WebGLTexelPremultiplicationOp::None)
|
||||
{
|
||||
// Should have used a fast exit path earlier, rather than entering this function.
|
||||
// we explicitly return here to allow the compiler to avoid generating this code
|
||||
return;
|
||||
}
|
||||
|
||||
// Only textures uploaded from DOM elements or ImageData can allow DstFormat != SrcFormat.
|
||||
// DOM elements can only give BGRA8, BGRX8, A8, RGB565 formats. See DOMElementToImageSurface.
|
||||
// ImageData is always RGBA8. So all other SrcFormat will always satisfy DstFormat==SrcFormat,
|
||||
// so we can avoid compiling the code for all the unreachable paths.
|
||||
//cjh const bool CanSrcFormatComeFromDOMElementOrImageData
|
||||
// = SrcFormat == WebGLTexelFormat::BGRA8 ||
|
||||
// SrcFormat == WebGLTexelFormat::BGRX8 ||
|
||||
// SrcFormat == WebGLTexelFormat::A8 ||
|
||||
// SrcFormat == WebGLTexelFormat::RGB565 ||
|
||||
// SrcFormat == WebGLTexelFormat::RGBA8;
|
||||
// if (!CanSrcFormatComeFromDOMElementOrImageData &&
|
||||
// SrcFormat != DstFormat)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Likewise, only textures uploaded from DOM elements or ImageData can possibly have to be unpremultiplied.
|
||||
// if (!CanSrcFormatComeFromDOMElementOrImageData &&
|
||||
// PremultiplicationOp == WebGLTexelPremultiplicationOp::Unpremultiply)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// there is no point in premultiplication/unpremultiplication
|
||||
// in the following cases:
|
||||
// - the source format has no alpha
|
||||
// - the source format has no color
|
||||
// - the destination format has no color
|
||||
if (!HasAlpha(SrcFormat) ||
|
||||
!HasColor(SrcFormat) ||
|
||||
!HasColor(DstFormat))
|
||||
{
|
||||
|
||||
if (PremultiplicationOp != WebGLTexelPremultiplicationOp::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// end of early return cases.
|
||||
|
||||
MOZ_ASSERT(!mAlreadyRun, "converter should be run only once!");
|
||||
mAlreadyRun = true;
|
||||
|
||||
// gather some compile-time meta-data about the formats at hand.
|
||||
|
||||
typedef
|
||||
typename DataTypeForFormat<SrcFormat>::Type
|
||||
SrcType;
|
||||
typedef
|
||||
typename DataTypeForFormat<DstFormat>::Type
|
||||
DstType;
|
||||
|
||||
const WebGLTexelFormat IntermediateSrcFormat
|
||||
= IntermediateFormat<SrcFormat>::Value;
|
||||
const WebGLTexelFormat IntermediateDstFormat
|
||||
= IntermediateFormat<DstFormat>::Value;
|
||||
typedef
|
||||
typename DataTypeForFormat<IntermediateSrcFormat>::Type
|
||||
IntermediateSrcType;
|
||||
typedef
|
||||
typename DataTypeForFormat<IntermediateDstFormat>::Type
|
||||
IntermediateDstType;
|
||||
|
||||
const size_t NumElementsPerSrcTexel = NumElementsPerTexelForFormat<SrcFormat>();
|
||||
const size_t NumElementsPerDstTexel = NumElementsPerTexelForFormat<DstFormat>();
|
||||
const size_t MaxElementsPerTexel = 4;
|
||||
MOZ_ASSERT(NumElementsPerSrcTexel <= MaxElementsPerTexel, "unhandled format");
|
||||
MOZ_ASSERT(NumElementsPerDstTexel <= MaxElementsPerTexel, "unhandled format");
|
||||
|
||||
// we assume that the strides are multiples of the sizeof of respective types.
|
||||
// this assumption will allow us to iterate over src and dst images using typed
|
||||
// pointers, e.g. uint8_t* or uint16_t* or float*, instead of untyped pointers.
|
||||
// So this assumption allows us to write cleaner and safer code, but it might
|
||||
// not be true forever and if it eventually becomes wrong, we'll have to revert
|
||||
// to always iterating using uint8_t* pointers regardless of the types at hand.
|
||||
MOZ_ASSERT(mSrcStride % sizeof(SrcType) == 0 &&
|
||||
mDstStride % sizeof(DstType) == 0,
|
||||
"Unsupported: texture stride is not a multiple of sizeof(type)");
|
||||
const ptrdiff_t srcStrideInElements = mSrcStride / sizeof(SrcType);
|
||||
const ptrdiff_t dstStrideInElements = mDstStride / sizeof(DstType);
|
||||
|
||||
const SrcType* srcRowStart = static_cast<const SrcType*>(mSrcStart);
|
||||
DstType* dstRowStart = static_cast<DstType*>(mDstStart);
|
||||
|
||||
// the loop performing the texture format conversion
|
||||
for (size_t i = 0; i < mHeight; ++i) {
|
||||
const SrcType* srcRowEnd = srcRowStart + mWidth * NumElementsPerSrcTexel;
|
||||
const SrcType* srcPtr = srcRowStart;
|
||||
DstType* dstPtr = dstRowStart;
|
||||
while (srcPtr != srcRowEnd) {
|
||||
// convert a single texel. We proceed in 3 steps: unpack the source texel
|
||||
// so the corresponding interchange format (e.g. unpack RGB565 to RGBA8),
|
||||
// convert the resulting data type to the destination type (e.g. convert
|
||||
// from RGBA8 to RGBA32F), and finally pack the destination texel
|
||||
// (e.g. pack RGBA32F to RGB32F).
|
||||
IntermediateSrcType unpackedSrc[MaxElementsPerTexel];
|
||||
IntermediateDstType unpackedDst[MaxElementsPerTexel];
|
||||
|
||||
// unpack a src texel to corresponding intermediate src format.
|
||||
// for example, unpack RGB565 to RGBA8
|
||||
unpack<SrcFormat>(srcPtr, unpackedSrc);
|
||||
// convert the data type to the destination type, if needed.
|
||||
// for example, convert RGBA8 to RGBA32F
|
||||
convertType(unpackedSrc, unpackedDst);
|
||||
// pack the destination texel.
|
||||
// for example, pack RGBA32F to RGB32F
|
||||
// pack<DstFormat, PremultiplicationOp>(unpackedDst, dstPtr);
|
||||
|
||||
srcPtr += NumElementsPerSrcTexel;
|
||||
dstPtr += NumElementsPerDstTexel;
|
||||
}
|
||||
srcRowStart += srcStrideInElements;
|
||||
dstRowStart += dstStrideInElements;
|
||||
}
|
||||
|
||||
mSuccess = true;
|
||||
}
|
||||
|
||||
template<WebGLTexelFormat SrcFormat,
|
||||
WebGLTexelFormat DstFormat>
|
||||
void run(WebGLTexelPremultiplicationOp premultiplicationOp)
|
||||
{
|
||||
#define WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(PremultiplicationOp) \
|
||||
case PremultiplicationOp: \
|
||||
return run<SrcFormat, DstFormat, PremultiplicationOp>();
|
||||
|
||||
switch (premultiplicationOp) {
|
||||
WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::None)
|
||||
WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::Premultiply)
|
||||
WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::Unpremultiply)
|
||||
default:
|
||||
MOZ_ASSERT(false, "unhandled case. Coding mistake?");
|
||||
}
|
||||
|
||||
#undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP
|
||||
}
|
||||
|
||||
template<WebGLTexelFormat SrcFormat>
|
||||
void run(WebGLTexelFormat dstFormat,
|
||||
WebGLTexelPremultiplicationOp premultiplicationOp)
|
||||
{
|
||||
#define WEBGLIMAGECONVERTER_CASE_DSTFORMAT(DstFormat) \
|
||||
case DstFormat: \
|
||||
return run<SrcFormat, DstFormat>(premultiplicationOp);
|
||||
|
||||
switch (dstFormat) {
|
||||
// 1-channel formats
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A8)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A16F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A32F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R8)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R16F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R32F)
|
||||
// 2-channel formats
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA8)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA16F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA32F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG8)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG16F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RG32F)
|
||||
// 3-channel formats
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB565)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB8)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB11F11F10F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB16F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB32F)
|
||||
// 4-channel formats
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA4444)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA5551)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA8)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA16F)
|
||||
WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA32F)
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false, "unhandled case. Coding mistake?");
|
||||
}
|
||||
|
||||
#undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void run(WebGLTexelFormat srcFormat,
|
||||
WebGLTexelFormat dstFormat,
|
||||
WebGLTexelPremultiplicationOp premultiplicationOp)
|
||||
{
|
||||
#define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
|
||||
case SrcFormat: \
|
||||
return run<SrcFormat>(dstFormat, premultiplicationOp);
|
||||
|
||||
switch (srcFormat) {
|
||||
// 1-channel formats
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A8)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A16F)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A32F)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R8)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R16F)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R32F)
|
||||
// 2-channel formats
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA8)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA16F)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA32F)
|
||||
// 3-channel formats
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB565)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB8)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB16F)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB32F)
|
||||
// 4-channel formats
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA4444)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA5551)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA8)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA16F)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA32F)
|
||||
// DOM element source formats
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRX8)
|
||||
WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRA8)
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false, "unhandled case. Coding mistake?");
|
||||
}
|
||||
|
||||
#undef WEBGLIMAGECONVERTER_CASE_SRCFORMAT
|
||||
}
|
||||
|
||||
WebGLImageConverter(size_t width, size_t height,
|
||||
const void* srcStart, void* dstStart,
|
||||
ptrdiff_t srcStride, ptrdiff_t dstStride)
|
||||
: mWidth(width), mHeight(height),
|
||||
mSrcStart(srcStart), mDstStart(dstStart),
|
||||
mSrcStride(srcStride), mDstStride(dstStride),
|
||||
mAlreadyRun(false), mSuccess(false)
|
||||
{}
|
||||
|
||||
bool Success() const {
|
||||
return mSuccess;
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
bool
|
||||
ConvertImage(size_t width, size_t height,
|
||||
const void* srcBegin, size_t srcStride, gl::OriginPos srcOrigin,
|
||||
WebGLTexelFormat srcFormat, bool srcPremultiplied,
|
||||
void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin,
|
||||
WebGLTexelFormat dstFormat, bool dstPremultiplied,
|
||||
bool* const out_wasTrivial)
|
||||
{
|
||||
*out_wasTrivial = true;
|
||||
|
||||
if (srcFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion ||
|
||||
dstFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!width || !height)
|
||||
return true;
|
||||
|
||||
const bool shouldYFlip = (srcOrigin != dstOrigin);
|
||||
|
||||
const bool canSkipPremult = (!HasAlpha(srcFormat) ||
|
||||
!HasColor(srcFormat) ||
|
||||
!HasColor(dstFormat));
|
||||
|
||||
WebGLTexelPremultiplicationOp premultOp;
|
||||
if (canSkipPremult) {
|
||||
premultOp = WebGLTexelPremultiplicationOp::None;
|
||||
} else if (!srcPremultiplied && dstPremultiplied) {
|
||||
premultOp = WebGLTexelPremultiplicationOp::Premultiply;
|
||||
} else if (srcPremultiplied && !dstPremultiplied) {
|
||||
premultOp = WebGLTexelPremultiplicationOp::Unpremultiply;
|
||||
} else {
|
||||
premultOp = WebGLTexelPremultiplicationOp::None;
|
||||
}
|
||||
|
||||
const uint8_t* srcItr = (const uint8_t*)srcBegin;
|
||||
const uint8_t* const srcEnd = srcItr + srcStride * height;
|
||||
uint8_t* dstItr = (uint8_t*)dstBegin;
|
||||
ptrdiff_t dstItrStride = dstStride;
|
||||
if (shouldYFlip) {
|
||||
dstItr = dstItr + dstStride * (height - 1);
|
||||
dstItrStride = -dstItrStride;
|
||||
}
|
||||
|
||||
if (srcFormat == dstFormat && premultOp == WebGLTexelPremultiplicationOp::None) {
|
||||
// Fast exit path: we just have to memcpy all the rows.
|
||||
//
|
||||
// The case where absolutely nothing needs to be done is supposed to have
|
||||
// been handled earlier (in TexImage2D_base, etc).
|
||||
//
|
||||
// So the case we're handling here is when even though no format conversion is
|
||||
// needed, we still might have to flip vertically and/or to adjust to a different
|
||||
// stride.
|
||||
|
||||
// We ignore canSkipPremult for this perf trap, since it's an avoidable perf cliff
|
||||
// under the WebGL API user's control.
|
||||
MOZ_ASSERT((srcPremultiplied != dstPremultiplied ||
|
||||
shouldYFlip ||
|
||||
srcStride != dstStride),
|
||||
"Performance trap -- should handle this case earlier to avoid memcpy");
|
||||
|
||||
const auto bytesPerPixel = TexelBytesForFormat(srcFormat);
|
||||
const size_t bytesPerRow = bytesPerPixel * width;
|
||||
|
||||
while (srcItr != srcEnd) {
|
||||
memcpy(dstItr, srcItr, bytesPerRow);
|
||||
srcItr += srcStride;
|
||||
dstItr += dstItrStride;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
*out_wasTrivial = false;
|
||||
|
||||
WebGLImageConverter converter(width, height, srcItr, dstItr, srcStride, dstItrStride);
|
||||
converter.run(srcFormat, dstFormat, premultOp);
|
||||
|
||||
if (!converter.Success()) {
|
||||
// the dst image may be left uninitialized, so we better not try to
|
||||
// continue even in release builds. This should never happen anyway,
|
||||
// and would be a bug in our code.
|
||||
MOZ_CRASH("programming mistake in WebGL texture conversions");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end namespace mozilla
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,228 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef WEBGLTYPES_H_
|
||||
#define WEBGLTYPES_H_
|
||||
|
||||
// Most WebIDL typedefs are identical to their OpenGL counterparts.
|
||||
//cjh #include "GLTypes.h"
|
||||
#include "platform/CCGL.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Manual reflection of WebIDL typedefs that are different from their
|
||||
// OpenGL counterparts.
|
||||
typedef int64_t WebGLsizeiptr;
|
||||
typedef int64_t WebGLintptr;
|
||||
typedef bool WebGLboolean;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
class GLContext; // This is going to be needed a lot.
|
||||
} // namespace gl
|
||||
|
||||
/*
|
||||
* WebGLTextureFakeBlackStatus is an enum to track what needs to use a dummy 1x1 black
|
||||
* texture, which we refer to as a 'fake black' texture.
|
||||
*
|
||||
* There are two things that can cause us to use such 'fake black' textures:
|
||||
*
|
||||
* (1) OpenGL ES rules on sampling incomplete textures specify that they
|
||||
* must be sampled as RGBA(0, 0, 0, 1) (opaque black). We have to implement these rules
|
||||
* ourselves, if only because we do not always run on OpenGL ES, and also
|
||||
* because this is dangerously close to the kind of case where we don't
|
||||
* want to trust the driver with corner cases of texture memory accesses.
|
||||
*
|
||||
* (2) OpenGL has cases where a renderbuffer, or a texture image, can contain
|
||||
* uninitialized image data. See below the comment about WebGLImageDataStatus.
|
||||
* WebGL must never have access to uninitialized image data. The WebGL 1 spec,
|
||||
* section 4.1 'Resource Restrictions', specifies that in any such case, the
|
||||
* uninitialized image data must be exposed to WebGL as if it were filled
|
||||
* with zero bytes, which means it's either opaque or transparent black
|
||||
* depending on whether the image format has alpha.
|
||||
*/
|
||||
|
||||
enum class FakeBlackType : uint8_t {
|
||||
None,
|
||||
RGBA0001, // Incomplete textures and uninitialized no-alpha color textures.
|
||||
RGBA0000, // Uninitialized with-alpha color textures.
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementing WebGL (or OpenGL ES 2.0) on top of desktop OpenGL requires
|
||||
* emulating the vertex attrib 0 array when it's not enabled. Indeed,
|
||||
* OpenGL ES 2.0 allows drawing without vertex attrib 0 array enabled, but
|
||||
* desktop OpenGL does not allow that.
|
||||
*/
|
||||
enum class WebGLVertexAttrib0Status : uint8_t {
|
||||
Default, // default status - no emulation needed
|
||||
EmulatedUninitializedArray, // need an artificial attrib 0 array, but contents may be left uninitialized
|
||||
EmulatedInitializedArray // need an artificial attrib 0 array, and contents must be initialized
|
||||
};
|
||||
|
||||
/*
|
||||
* Enum to track the status of image data (renderbuffer or texture image) presence
|
||||
* and initialization.
|
||||
*
|
||||
* - NoImageData is the initial state before any image data is allocated.
|
||||
* - InitializedImageData is the state after image data is allocated and initialized.
|
||||
* - UninitializedImageData is an intermediate state where data is allocated but not
|
||||
* initialized. It is the state that renderbuffers are in after a renderbufferStorage call,
|
||||
* and it is the state that texture images are in after a texImage2D call with null data.
|
||||
*/
|
||||
enum class WebGLImageDataStatus : uint8_t {
|
||||
NoImageData,
|
||||
UninitializedImageData,
|
||||
InitializedImageData
|
||||
};
|
||||
|
||||
/*
|
||||
* The formats that may participate, either as source or destination formats,
|
||||
* in WebGL texture conversions. This includes:
|
||||
* - all the formats accepted by WebGL.texImage2D, e.g. RGBA4444
|
||||
* - additional formats provided by extensions, e.g. RGB32F
|
||||
* - additional source formats, depending on browser details, used when uploading
|
||||
* textures from DOM elements. See gfxImageSurface::Format().
|
||||
*/
|
||||
enum class WebGLTexelFormat : uint8_t {
|
||||
// returned by SurfaceFromElementResultToImageSurface to indicate absence of image data
|
||||
None,
|
||||
// common value for formats for which format conversions are not supported
|
||||
FormatNotSupportingAnyConversion,
|
||||
// dummy pseudo-format meaning "use the other format".
|
||||
// For example, if SrcFormat=Auto and DstFormat=RGB8, then the source
|
||||
// is implicitly treated as being RGB8 itself.
|
||||
Auto,
|
||||
// 1-channel formats
|
||||
A8,
|
||||
A16F, // OES_texture_half_float
|
||||
A32F, // OES_texture_float
|
||||
R8,
|
||||
R16F, // OES_texture_half_float
|
||||
R32F, // OES_texture_float
|
||||
// 2-channel formats
|
||||
RA8,
|
||||
RA16F, // OES_texture_half_float
|
||||
RA32F, // OES_texture_float
|
||||
RG8,
|
||||
RG16F,
|
||||
RG32F,
|
||||
// 3-channel formats
|
||||
RGB8,
|
||||
RGB565,
|
||||
RGB11F11F10F,
|
||||
RGB16F, // OES_texture_half_float
|
||||
RGB32F, // OES_texture_float
|
||||
// 4-channel formats
|
||||
RGBA8,
|
||||
RGBA5551,
|
||||
RGBA4444,
|
||||
RGBA16F, // OES_texture_half_float
|
||||
RGBA32F, // OES_texture_float
|
||||
// DOM element source only formats.
|
||||
RGBX8,
|
||||
BGRX8,
|
||||
BGRA8
|
||||
};
|
||||
|
||||
enum class WebGLTexImageFunc : uint8_t {
|
||||
TexImage,
|
||||
TexSubImage,
|
||||
CopyTexImage,
|
||||
CopyTexSubImage,
|
||||
CompTexImage,
|
||||
CompTexSubImage,
|
||||
};
|
||||
|
||||
enum class WebGLTexDimensions : uint8_t {
|
||||
Tex2D,
|
||||
Tex3D
|
||||
};
|
||||
|
||||
// Please keep extensions in alphabetic order.
|
||||
enum class WebGLExtensionID : uint8_t {
|
||||
ANGLE_instanced_arrays,
|
||||
EXT_blend_minmax,
|
||||
EXT_color_buffer_float,
|
||||
EXT_color_buffer_half_float,
|
||||
EXT_frag_depth,
|
||||
EXT_sRGB,
|
||||
EXT_shader_texture_lod,
|
||||
EXT_texture_filter_anisotropic,
|
||||
EXT_disjoint_timer_query,
|
||||
MOZ_debug,
|
||||
OES_element_index_uint,
|
||||
OES_standard_derivatives,
|
||||
OES_texture_float,
|
||||
OES_texture_float_linear,
|
||||
OES_texture_half_float,
|
||||
OES_texture_half_float_linear,
|
||||
OES_vertex_array_object,
|
||||
WEBGL_color_buffer_float,
|
||||
WEBGL_compressed_texture_astc,
|
||||
WEBGL_compressed_texture_atc,
|
||||
WEBGL_compressed_texture_etc,
|
||||
WEBGL_compressed_texture_etc1,
|
||||
WEBGL_compressed_texture_pvrtc,
|
||||
WEBGL_compressed_texture_s3tc,
|
||||
WEBGL_compressed_texture_s3tc_srgb,
|
||||
WEBGL_debug_renderer_info,
|
||||
WEBGL_debug_shaders,
|
||||
WEBGL_depth_texture,
|
||||
WEBGL_draw_buffers,
|
||||
WEBGL_lose_context,
|
||||
Max,
|
||||
Unknown
|
||||
};
|
||||
|
||||
class UniqueBuffer
|
||||
{
|
||||
// Like UniquePtr<>, but for void* and malloc/calloc/free.
|
||||
void* mBuffer;
|
||||
|
||||
public:
|
||||
UniqueBuffer()
|
||||
: mBuffer(nullptr)
|
||||
{ }
|
||||
|
||||
MOZ_IMPLICIT UniqueBuffer(void* buffer)
|
||||
: mBuffer(buffer)
|
||||
{ }
|
||||
|
||||
~UniqueBuffer() {
|
||||
free(mBuffer);
|
||||
}
|
||||
|
||||
UniqueBuffer(UniqueBuffer&& other) {
|
||||
this->mBuffer = other.mBuffer;
|
||||
other.mBuffer = nullptr;
|
||||
}
|
||||
|
||||
UniqueBuffer& operator =(UniqueBuffer&& other) {
|
||||
free(this->mBuffer);
|
||||
this->mBuffer = other.mBuffer;
|
||||
other.mBuffer = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
UniqueBuffer& operator =(void* newBuffer) {
|
||||
free(this->mBuffer);
|
||||
this->mBuffer = newBuffer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return bool(mBuffer); }
|
||||
|
||||
void* get() const { return mBuffer; }
|
||||
|
||||
UniqueBuffer(const UniqueBuffer& other) = delete; // construct using Move()!
|
||||
void operator =(const UniqueBuffer& other) = delete; // assign using Move()!
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
MOZ_BEGIN_EXTERN_C
|
||||
|
||||
/*
|
||||
* The crash reason is defined as a global variable here rather than in the
|
||||
* crash reporter itself to make it available to all code, even libraries like
|
||||
* JS that don't link with the crash reporter directly. This value will only
|
||||
* be consumed if the crash reporter is used by the target application.
|
||||
*/
|
||||
MFBT_DATA const char* gMozCrashReason = nullptr;
|
||||
|
||||
#ifndef DEBUG
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
|
||||
MOZ_CrashOOL(int aLine, const char* aReason)
|
||||
#else
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
|
||||
MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason)
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ReportCrash(aReason, aFilename, aLine);
|
||||
#endif
|
||||
gMozCrashReason = aReason;
|
||||
MOZ_REALLY_CRASH(aLine);
|
||||
}
|
||||
|
||||
static char sPrintfCrashReason[sPrintfCrashReasonSize] = {};
|
||||
static mozilla::Atomic<bool> sCrashing(false);
|
||||
|
||||
#ifndef DEBUG
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void
|
||||
MOZ_CrashPrintf(int aLine, const char* aFormat, ...)
|
||||
#else
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void
|
||||
MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...)
|
||||
#endif
|
||||
{
|
||||
if (!sCrashing.compareExchange(false, true)) {
|
||||
// In the unlikely event of a race condition, skip
|
||||
// setting the crash reason and just crash safely.
|
||||
MOZ_REALLY_CRASH(aLine);
|
||||
}
|
||||
va_list aArgs;
|
||||
va_start(aArgs, aFormat);
|
||||
int ret = vsnprintf(sPrintfCrashReason, sPrintfCrashReasonSize,
|
||||
aFormat, aArgs);
|
||||
va_end(aArgs);
|
||||
MOZ_RELEASE_ASSERT(ret >= 0 && size_t(ret) < sPrintfCrashReasonSize,
|
||||
"Could not write the explanation string to the supplied buffer!");
|
||||
#ifdef DEBUG
|
||||
MOZ_ReportCrash(sPrintfCrashReason, aFilename, aLine);
|
||||
#endif
|
||||
gMozCrashReason = sPrintfCrashReason;
|
||||
MOZ_REALLY_CRASH(aLine);
|
||||
}
|
||||
|
||||
MOZ_END_EXTERN_C
|
||||
|
|
@ -0,0 +1,659 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Implementations of runtime and static assertion macros for C and C++. */
|
||||
|
||||
#ifndef mozilla_Assertions_h
|
||||
#define mozilla_Assertions_h
|
||||
|
||||
#if defined(MOZILLA_INTERNAL_API) && defined(__cplusplus)
|
||||
#define MOZ_DUMP_ASSERTION_STACK
|
||||
#endif
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/MacroArgs.h"
|
||||
#include "mozilla/StaticAnalysisFunctions.h"
|
||||
#include "mozilla/Types.h"
|
||||
#ifdef MOZ_DUMP_ASSERTION_STACK
|
||||
#include "nsTraceRefcnt.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The crash reason set by MOZ_CRASH_ANNOTATE is consumed by the crash reporter
|
||||
* if present. It is declared here (and defined in Assertions.cpp) to make it
|
||||
* available to all code, even libraries that don't link with the crash reporter
|
||||
* directly.
|
||||
*/
|
||||
MOZ_BEGIN_EXTERN_C
|
||||
extern MFBT_DATA const char* gMozCrashReason;
|
||||
MOZ_END_EXTERN_C
|
||||
|
||||
#if !defined(DEBUG) && (defined(MOZ_HAS_MOZGLUE) || defined(MOZILLA_INTERNAL_API))
|
||||
static inline void
|
||||
AnnotateMozCrashReason(const char* reason)
|
||||
{
|
||||
gMozCrashReason = reason;
|
||||
}
|
||||
# define MOZ_CRASH_ANNOTATE(...) AnnotateMozCrashReason(__VA_ARGS__)
|
||||
#else
|
||||
# define MOZ_CRASH_ANNOTATE(...) do { /* nothing */ } while (0)
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _MSC_VER
|
||||
/*
|
||||
* TerminateProcess and GetCurrentProcess are defined in <winbase.h>, which
|
||||
* further depends on <windef.h>. We hardcode these few definitions manually
|
||||
* because those headers clutter the global namespace with a significant
|
||||
* number of undesired macros and symbols.
|
||||
*/
|
||||
MOZ_BEGIN_EXTERN_C
|
||||
__declspec(dllimport) int __stdcall
|
||||
TerminateProcess(void* hProcess, unsigned int uExitCode);
|
||||
__declspec(dllimport) void* __stdcall GetCurrentProcess(void);
|
||||
MOZ_END_EXTERN_C
|
||||
#else
|
||||
# include <signal.h>
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
# include <android/log.h>
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define MOZ_UNUSED_ATTRIBUTE __attribute__((unused))
|
||||
#else
|
||||
# define MOZ_UNUSED_ATTRIBUTE /* nothing */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_STATIC_ASSERT may be used to assert a condition *at compile time* in C.
|
||||
* In C++11, static_assert is provided by the compiler to the same effect.
|
||||
* This can be useful when you make certain assumptions about what must hold for
|
||||
* optimal, or even correct, behavior. For example, you might assert that the
|
||||
* size of a struct is a multiple of the target architecture's word size:
|
||||
*
|
||||
* struct S { ... };
|
||||
* // C
|
||||
* MOZ_STATIC_ASSERT(sizeof(S) % sizeof(size_t) == 0,
|
||||
* "S should be a multiple of word size for efficiency");
|
||||
* // C++11
|
||||
* static_assert(sizeof(S) % sizeof(size_t) == 0,
|
||||
* "S should be a multiple of word size for efficiency");
|
||||
*
|
||||
* This macro can be used in any location where both an extern declaration and a
|
||||
* typedef could be used.
|
||||
*/
|
||||
#ifndef __cplusplus
|
||||
/*
|
||||
* Some of the definitions below create an otherwise-unused typedef. This
|
||||
* triggers compiler warnings with some versions of gcc, so mark the typedefs
|
||||
* as permissibly-unused to disable the warnings.
|
||||
*/
|
||||
# define MOZ_STATIC_ASSERT_GLUE1(x, y) x##y
|
||||
# define MOZ_STATIC_ASSERT_GLUE(x, y) MOZ_STATIC_ASSERT_GLUE1(x, y)
|
||||
# if defined(__SUNPRO_CC)
|
||||
/*
|
||||
* The Sun Studio C++ compiler is buggy when declaring, inside a function,
|
||||
* another extern'd function with an array argument whose length contains a
|
||||
* sizeof, triggering the error message "sizeof expression not accepted as
|
||||
* size of array parameter". This bug (6688515, not public yet) would hit
|
||||
* defining moz_static_assert as a function, so we always define an extern
|
||||
* array for Sun Studio.
|
||||
*
|
||||
* We include the line number in the symbol name in a best-effort attempt
|
||||
* to avoid conflicts (see below).
|
||||
*/
|
||||
# define MOZ_STATIC_ASSERT(cond, reason) \
|
||||
extern char MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __LINE__)[(cond) ? 1 : -1]
|
||||
# elif defined(__COUNTER__)
|
||||
/*
|
||||
* If there was no preferred alternative, use a compiler-agnostic version.
|
||||
*
|
||||
* Note that the non-__COUNTER__ version has a bug in C++: it can't be used
|
||||
* in both |extern "C"| and normal C++ in the same translation unit. (Alas
|
||||
* |extern "C"| isn't allowed in a function.) The only affected compiler
|
||||
* we really care about is gcc 4.2. For that compiler and others like it,
|
||||
* we include the line number in the function name to do the best we can to
|
||||
* avoid conflicts. These should be rare: a conflict would require use of
|
||||
* MOZ_STATIC_ASSERT on the same line in separate files in the same
|
||||
* translation unit, *and* the uses would have to be in code with
|
||||
* different linkage, *and* the first observed use must be in C++-linkage
|
||||
* code.
|
||||
*/
|
||||
# define MOZ_STATIC_ASSERT(cond, reason) \
|
||||
typedef int MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __COUNTER__)[(cond) ? 1 : -1] MOZ_UNUSED_ATTRIBUTE
|
||||
# else
|
||||
# define MOZ_STATIC_ASSERT(cond, reason) \
|
||||
extern void MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __LINE__)(int arg[(cond) ? 1 : -1]) MOZ_UNUSED_ATTRIBUTE
|
||||
# endif
|
||||
|
||||
#define MOZ_STATIC_ASSERT_IF(cond, expr, reason) MOZ_STATIC_ASSERT(!(cond) || (expr), reason)
|
||||
#else
|
||||
#define MOZ_STATIC_ASSERT_IF(cond, expr, reason) static_assert(!(cond) || (expr), reason)
|
||||
#endif
|
||||
|
||||
MOZ_BEGIN_EXTERN_C
|
||||
|
||||
/*
|
||||
* Prints |aStr| as an assertion failure (using aFilename and aLine as the
|
||||
* location of the assertion) to the standard debug-output channel.
|
||||
*
|
||||
* Usually you should use MOZ_ASSERT or MOZ_CRASH instead of this method. This
|
||||
* method is primarily for internal use in this header, and only secondarily
|
||||
* for use in implementing release-build assertions.
|
||||
*/
|
||||
MOZ_MAYBE_UNUSED static MOZ_COLD MOZ_NEVER_INLINE void
|
||||
MOZ_ReportAssertionFailure(const char* aStr, const char* aFilename, int aLine)
|
||||
MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS
|
||||
{
|
||||
#ifdef ANDROID
|
||||
__android_log_print(ANDROID_LOG_FATAL, "MOZ_Assert",
|
||||
"Assertion failure: %s, at %s:%d\n",
|
||||
aStr, aFilename, aLine);
|
||||
#else
|
||||
fprintf(stderr, "Assertion failure: %s, at %s:%d\n", aStr, aFilename, aLine);
|
||||
#if defined (MOZ_DUMP_ASSERTION_STACK)
|
||||
nsTraceRefcnt::WalkTheStack(stderr);
|
||||
#endif
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
MOZ_MAYBE_UNUSED static MOZ_COLD MOZ_NEVER_INLINE void
|
||||
MOZ_ReportCrash(const char* aStr, const char* aFilename, int aLine)
|
||||
MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS
|
||||
{
|
||||
#ifdef ANDROID
|
||||
__android_log_print(ANDROID_LOG_FATAL, "MOZ_CRASH",
|
||||
"Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine);
|
||||
#else
|
||||
fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine);
|
||||
#if defined(MOZ_DUMP_ASSERTION_STACK)
|
||||
nsTraceRefcnt::WalkTheStack(stderr);
|
||||
#endif
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* MOZ_REALLY_CRASH is used in the implementation of MOZ_CRASH(). You should
|
||||
* call MOZ_CRASH instead.
|
||||
*/
|
||||
#if defined(_MSC_VER)
|
||||
/*
|
||||
* On MSVC use the __debugbreak compiler intrinsic, which produces an inline
|
||||
* (not nested in a system function) breakpoint. This distinctively invokes
|
||||
* Breakpad without requiring system library symbols on all stack-processing
|
||||
* machines, as a nested breakpoint would require.
|
||||
*
|
||||
* We use __LINE__ to prevent the compiler from folding multiple crash sites
|
||||
* together, which would make crash reports hard to understand.
|
||||
*
|
||||
* We use TerminateProcess with the exit code aborting would generate
|
||||
* because we don't want to invoke atexit handlers, destructors, library
|
||||
* unload handlers, and so on when our process might be in a compromised
|
||||
* state.
|
||||
*
|
||||
* We don't use abort() because it'd cause Windows to annoyingly pop up the
|
||||
* process error dialog multiple times. See bug 345118 and bug 426163.
|
||||
*
|
||||
* (Technically these are Windows requirements, not MSVC requirements. But
|
||||
* practically you need MSVC for debugging, and we only ship builds created
|
||||
* by MSVC, so doing it this way reduces complexity.)
|
||||
*/
|
||||
|
||||
static MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void MOZ_NoReturn(int aLine)
|
||||
{
|
||||
*((volatile int*) NULL) = aLine;
|
||||
TerminateProcess(GetCurrentProcess(), 3);
|
||||
}
|
||||
|
||||
# define MOZ_REALLY_CRASH(line) \
|
||||
do { \
|
||||
__debugbreak(); \
|
||||
MOZ_NoReturn(line); \
|
||||
} while (0)
|
||||
#else
|
||||
# ifdef __cplusplus
|
||||
# define MOZ_REALLY_CRASH(line) \
|
||||
do { \
|
||||
*((volatile int*) NULL) = line; \
|
||||
::abort(); \
|
||||
} while (0)
|
||||
# else
|
||||
# define MOZ_REALLY_CRASH(line) \
|
||||
do { \
|
||||
*((volatile int*) NULL) = line; \
|
||||
abort(); \
|
||||
} while (0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_CRASH([explanation-string]) crashes the program, plain and simple, in a
|
||||
* Breakpad-compatible way, in both debug and release builds.
|
||||
*
|
||||
* MOZ_CRASH is a good solution for "handling" failure cases when you're
|
||||
* unwilling or unable to handle them more cleanly -- for OOM, for likely memory
|
||||
* corruption, and so on. It's also a good solution if you need safe behavior
|
||||
* in release builds as well as debug builds. But if the failure is one that
|
||||
* should be debugged and fixed, MOZ_ASSERT is generally preferable.
|
||||
*
|
||||
* The optional explanation-string, if provided, must be a string literal
|
||||
* explaining why we're crashing. This argument is intended for use with
|
||||
* MOZ_CRASH() calls whose rationale is non-obvious; don't use it if it's
|
||||
* obvious why we're crashing.
|
||||
*
|
||||
* If we're a DEBUG build and we crash at a MOZ_CRASH which provides an
|
||||
* explanation-string, we print the string to stderr. Otherwise, we don't
|
||||
* print anything; this is because we want MOZ_CRASH to be 100% safe in release
|
||||
* builds, and it's hard to print to stderr safely when memory might have been
|
||||
* corrupted.
|
||||
*/
|
||||
#ifndef DEBUG
|
||||
# define MOZ_CRASH(...) \
|
||||
do { \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
|
||||
MOZ_REALLY_CRASH(__LINE__); \
|
||||
} while (0)
|
||||
#else
|
||||
# define MOZ_CRASH(...) \
|
||||
do { \
|
||||
MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
|
||||
MOZ_REALLY_CRASH(__LINE__); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_CRASH_UNSAFE_OOL(explanation-string) can be used if the explanation
|
||||
* string cannot be a string literal (but no other processing needs to be done
|
||||
* on it). A regular MOZ_CRASH() is preferred wherever possible, as passing
|
||||
* arbitrary strings from a potentially compromised process is not without risk.
|
||||
* If the string being passed is the result of a printf-style function,
|
||||
* consider using MOZ_CRASH_UNSAFE_PRINTF instead.
|
||||
*
|
||||
* @note This macro causes data collection because crash strings are annotated
|
||||
* to crash-stats and are publicly visible. Firefox data stewards must do data
|
||||
* review on usages of this macro.
|
||||
*/
|
||||
#ifndef DEBUG
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
|
||||
MOZ_CrashOOL(int aLine, const char* aReason);
|
||||
# define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__LINE__, reason)
|
||||
#else
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
|
||||
MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason);
|
||||
# define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__FILE__, __LINE__, reason)
|
||||
#endif
|
||||
|
||||
static const size_t sPrintfMaxArgs = 4;
|
||||
static const size_t sPrintfCrashReasonSize = 1024;
|
||||
|
||||
#ifndef DEBUG
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void
|
||||
MOZ_CrashPrintf(int aLine, const char* aFormat, ...);
|
||||
# define MOZ_CALL_CRASH_PRINTF(format, ...) \
|
||||
MOZ_CrashPrintf(__LINE__, format, __VA_ARGS__)
|
||||
#else
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void
|
||||
MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...);
|
||||
# define MOZ_CALL_CRASH_PRINTF(format, ...) \
|
||||
MOZ_CrashPrintf(__FILE__, __LINE__, format, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_CRASH_UNSAFE_PRINTF(format, arg1 [, args]) can be used when more
|
||||
* information is desired than a string literal can supply. The caller provides
|
||||
* a printf-style format string, which must be a string literal and between
|
||||
* 1 and 4 additional arguments. A regular MOZ_CRASH() is preferred wherever
|
||||
* possible, as passing arbitrary strings to printf from a potentially
|
||||
* compromised process is not without risk.
|
||||
*
|
||||
* @note This macro causes data collection because crash strings are annotated
|
||||
* to crash-stats and are publicly visible. Firefox data stewards must do data
|
||||
* review on usages of this macro.
|
||||
*/
|
||||
#define MOZ_CRASH_UNSAFE_PRINTF(format, ...) \
|
||||
do { \
|
||||
static_assert( \
|
||||
MOZ_ARG_COUNT(__VA_ARGS__) > 0, \
|
||||
"Did you forget arguments to MOZ_CRASH_UNSAFE_PRINTF? " \
|
||||
"Or maybe you want MOZ_CRASH instead?"); \
|
||||
static_assert( \
|
||||
MOZ_ARG_COUNT(__VA_ARGS__) <= sPrintfMaxArgs, \
|
||||
"Only up to 4 additional arguments are allowed!"); \
|
||||
static_assert(sizeof(format) <= sPrintfCrashReasonSize, \
|
||||
"The supplied format string is too long!"); \
|
||||
MOZ_CALL_CRASH_PRINTF("" format, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
MOZ_END_EXTERN_C
|
||||
|
||||
/*
|
||||
* MOZ_ASSERT(expr [, explanation-string]) asserts that |expr| must be truthy in
|
||||
* debug builds. If it is, execution continues. Otherwise, an error message
|
||||
* including the expression and the explanation-string (if provided) is printed,
|
||||
* an attempt is made to invoke any existing debugger, and execution halts.
|
||||
* MOZ_ASSERT is fatal: no recovery is possible. Do not assert a condition
|
||||
* which can correctly be falsy.
|
||||
*
|
||||
* The optional explanation-string, if provided, must be a string literal
|
||||
* explaining the assertion. It is intended for use with assertions whose
|
||||
* correctness or rationale is non-obvious, and for assertions where the "real"
|
||||
* condition being tested is best described prosaically. Don't provide an
|
||||
* explanation if it's not actually helpful.
|
||||
*
|
||||
* // No explanation needed: pointer arguments often must not be NULL.
|
||||
* MOZ_ASSERT(arg);
|
||||
*
|
||||
* // An explanation can be helpful to explain exactly how we know an
|
||||
* // assertion is valid.
|
||||
* MOZ_ASSERT(state == WAITING_FOR_RESPONSE,
|
||||
* "given that <thingA> and <thingB>, we must have...");
|
||||
*
|
||||
* // Or it might disambiguate multiple identical (save for their location)
|
||||
* // assertions of the same expression.
|
||||
* MOZ_ASSERT(getSlot(PRIMITIVE_THIS_SLOT).isUndefined(),
|
||||
* "we already set [[PrimitiveThis]] for this Boolean object");
|
||||
* MOZ_ASSERT(getSlot(PRIMITIVE_THIS_SLOT).isUndefined(),
|
||||
* "we already set [[PrimitiveThis]] for this String object");
|
||||
*
|
||||
* MOZ_ASSERT has no effect in non-debug builds. It is designed to catch bugs
|
||||
* *only* during debugging, not "in the field". If you want the latter, use
|
||||
* MOZ_RELEASE_ASSERT, which applies to non-debug builds as well.
|
||||
*
|
||||
* MOZ_DIAGNOSTIC_ASSERT works like MOZ_RELEASE_ASSERT in Nightly/Aurora and
|
||||
* MOZ_ASSERT in Beta/Release - use this when a condition is potentially rare
|
||||
* enough to require real user testing to hit, but is not security-sensitive.
|
||||
* This can cause user pain, so use it sparingly. If a MOZ_DIAGNOSTIC_ASSERT
|
||||
* is firing, it should promptly be converted to a MOZ_ASSERT while the failure
|
||||
* is being investigated, rather than letting users suffer.
|
||||
*
|
||||
* MOZ_DIAGNOSTIC_ASSERT_ENABLED is defined when MOZ_DIAGNOSTIC_ASSERT is like
|
||||
* MOZ_RELEASE_ASSERT rather than MOZ_ASSERT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implement MOZ_VALIDATE_ASSERT_CONDITION_TYPE, which is used to guard against
|
||||
* accidentally passing something unintended in lieu of an assertion condition.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include "mozilla/TypeTraits.h"
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
struct AssertionConditionType
|
||||
{
|
||||
typedef typename RemoveReference<T>::Type ValueT;
|
||||
static_assert(!IsArray<ValueT>::value,
|
||||
"Expected boolean assertion condition, got an array or a "
|
||||
"string!");
|
||||
static_assert(!IsFunction<ValueT>::value,
|
||||
"Expected boolean assertion condition, got a function! Did "
|
||||
"you intend to call that function?");
|
||||
static_assert(!IsFloatingPoint<ValueT>::value,
|
||||
"It's often a bad idea to assert that a floating-point number "
|
||||
"is nonzero, because such assertions tend to intermittently "
|
||||
"fail. Shouldn't your code gracefully handle this case instead "
|
||||
"of asserting? Anyway, if you really want to do that, write an "
|
||||
"explicit boolean condition, like !!x or x!=0.");
|
||||
|
||||
static const bool isValid = true;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
# define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x) \
|
||||
static_assert(mozilla::detail::AssertionConditionType<decltype(x)>::isValid, \
|
||||
"invalid assertion condition")
|
||||
#else
|
||||
# define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x)
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG) || defined(MOZ_ASAN)
|
||||
# define MOZ_REPORT_ASSERTION_FAILURE(...) MOZ_ReportAssertionFailure(__VA_ARGS__)
|
||||
#else
|
||||
# define MOZ_REPORT_ASSERTION_FAILURE(...) do { /* nothing */ } while (0)
|
||||
#endif
|
||||
|
||||
/* First the single-argument form. */
|
||||
#define MOZ_ASSERT_HELPER1(expr) \
|
||||
do { \
|
||||
MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
|
||||
if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \
|
||||
MOZ_REPORT_ASSERTION_FAILURE(#expr, __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ")"); \
|
||||
MOZ_REALLY_CRASH(__LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
/* Now the two-argument form. */
|
||||
#define MOZ_ASSERT_HELPER2(expr, explain) \
|
||||
do { \
|
||||
MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
|
||||
if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \
|
||||
MOZ_REPORT_ASSERTION_FAILURE(#expr " (" explain ")", __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ") (" explain ")"); \
|
||||
MOZ_REALLY_CRASH(__LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MOZ_RELEASE_ASSERT_GLUE(a, b) a b
|
||||
#define MOZ_RELEASE_ASSERT(...) \
|
||||
MOZ_RELEASE_ASSERT_GLUE( \
|
||||
MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \
|
||||
(__VA_ARGS__))
|
||||
|
||||
#ifdef DEBUG
|
||||
# define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__)
|
||||
#else
|
||||
# define MOZ_ASSERT(...) do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION)
|
||||
# define MOZ_DIAGNOSTIC_ASSERT MOZ_RELEASE_ASSERT
|
||||
# define MOZ_DIAGNOSTIC_ASSERT_ENABLED 1
|
||||
#else
|
||||
# define MOZ_DIAGNOSTIC_ASSERT MOZ_ASSERT
|
||||
# ifdef DEBUG
|
||||
# define MOZ_DIAGNOSTIC_ASSERT_ENABLED 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_ASSERT_IF(cond1, cond2) is equivalent to MOZ_ASSERT(cond2) if cond1 is
|
||||
* true.
|
||||
*
|
||||
* MOZ_ASSERT_IF(isPrime(num), num == 2 || isOdd(num));
|
||||
*
|
||||
* As with MOZ_ASSERT, MOZ_ASSERT_IF has effect only in debug builds. It is
|
||||
* designed to catch bugs during debugging, not "in the field".
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
# define MOZ_ASSERT_IF(cond, expr) \
|
||||
do { \
|
||||
if (cond) { \
|
||||
MOZ_ASSERT(expr); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
# define MOZ_ASSERT_IF(cond, expr) do { } while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_ASSUME_UNREACHABLE_MARKER() expands to an expression which states that
|
||||
* it is undefined behavior for execution to reach this point. No guarantees
|
||||
* are made about what will happen if this is reached at runtime. Most code
|
||||
* should use MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE because it has extra
|
||||
* asserts.
|
||||
*/
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
# define MOZ_ASSUME_UNREACHABLE_MARKER() __builtin_unreachable()
|
||||
#elif defined(_MSC_VER)
|
||||
# define MOZ_ASSUME_UNREACHABLE_MARKER() __assume(0)
|
||||
#else
|
||||
# ifdef __cplusplus
|
||||
# define MOZ_ASSUME_UNREACHABLE_MARKER() ::abort()
|
||||
# else
|
||||
# define MOZ_ASSUME_UNREACHABLE_MARKER() abort()
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE([reason]) tells the compiler that it
|
||||
* can assume that the macro call cannot be reached during execution. This lets
|
||||
* the compiler generate better-optimized code under some circumstances, at the
|
||||
* expense of the program's behavior being undefined if control reaches the
|
||||
* MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE.
|
||||
*
|
||||
* In Gecko, you probably should not use this macro outside of performance- or
|
||||
* size-critical code, because it's unsafe. If you don't care about code size
|
||||
* or performance, you should probably use MOZ_ASSERT or MOZ_CRASH.
|
||||
*
|
||||
* SpiderMonkey is a different beast, and there it's acceptable to use
|
||||
* MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE more widely.
|
||||
*
|
||||
* Note that MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE is noreturn, so it's valid
|
||||
* not to return a value following a MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE
|
||||
* call.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* enum ValueType {
|
||||
* VALUE_STRING,
|
||||
* VALUE_INT,
|
||||
* VALUE_FLOAT
|
||||
* };
|
||||
*
|
||||
* int ptrToInt(ValueType type, void* value) {
|
||||
* {
|
||||
* // We know for sure that type is either INT or FLOAT, and we want this
|
||||
* // code to run as quickly as possible.
|
||||
* switch (type) {
|
||||
* case VALUE_INT:
|
||||
* return *(int*) value;
|
||||
* case VALUE_FLOAT:
|
||||
* return (int) *(float*) value;
|
||||
* default:
|
||||
* MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected ValueType");
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
/*
|
||||
* Unconditional assert in debug builds for (assumed) unreachable code paths
|
||||
* that have a safe return without crashing in release builds.
|
||||
*/
|
||||
#define MOZ_ASSERT_UNREACHABLE(reason) \
|
||||
MOZ_ASSERT(false, "MOZ_ASSERT_UNREACHABLE: " reason)
|
||||
|
||||
#define MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(reason) \
|
||||
do { \
|
||||
MOZ_ASSERT_UNREACHABLE(reason); \
|
||||
MOZ_ASSUME_UNREACHABLE_MARKER(); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* MOZ_FALLTHROUGH_ASSERT is an annotation to suppress compiler warnings about
|
||||
* switch cases that MOZ_ASSERT(false) (or its alias MOZ_ASSERT_UNREACHABLE) in
|
||||
* debug builds, but intentionally fall through in release builds to handle
|
||||
* unexpected values.
|
||||
*
|
||||
* Why do we need MOZ_FALLTHROUGH_ASSERT in addition to MOZ_FALLTHROUGH? In
|
||||
* release builds, the MOZ_ASSERT(false) will expand to `do { } while (0)`,
|
||||
* requiring a MOZ_FALLTHROUGH annotation to suppress a -Wimplicit-fallthrough
|
||||
* warning. In debug builds, the MOZ_ASSERT(false) will expand to something like
|
||||
* `if (true) { MOZ_CRASH(); }` and the MOZ_FALLTHROUGH annotation will cause
|
||||
* a -Wunreachable-code warning. The MOZ_FALLTHROUGH_ASSERT macro breaks this
|
||||
* warning stalemate.
|
||||
*
|
||||
* // Example before MOZ_FALLTHROUGH_ASSERT:
|
||||
* switch (foo) {
|
||||
* default:
|
||||
* // This case wants to assert in debug builds, fall through in release.
|
||||
* MOZ_ASSERT(false); // -Wimplicit-fallthrough warning in release builds!
|
||||
* MOZ_FALLTHROUGH; // but -Wunreachable-code warning in debug builds!
|
||||
* case 5:
|
||||
* return 5;
|
||||
* }
|
||||
*
|
||||
* // Example with MOZ_FALLTHROUGH_ASSERT:
|
||||
* switch (foo) {
|
||||
* default:
|
||||
* // This case asserts in debug builds, falls through in release.
|
||||
* MOZ_FALLTHROUGH_ASSERT("Unexpected foo value?!");
|
||||
* case 5:
|
||||
* return 5;
|
||||
* }
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
# define MOZ_FALLTHROUGH_ASSERT(reason) MOZ_CRASH("MOZ_FALLTHROUGH_ASSERT: " reason)
|
||||
#else
|
||||
# define MOZ_FALLTHROUGH_ASSERT(...) MOZ_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_ALWAYS_TRUE(expr) and MOZ_ALWAYS_FALSE(expr) always evaluate the provided
|
||||
* expression, in debug builds and in release builds both. Then, in debug
|
||||
* builds only, the value of the expression is asserted either true or false
|
||||
* using MOZ_ASSERT.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
# define MOZ_ALWAYS_TRUE(expr) \
|
||||
do { \
|
||||
if ((expr)) { \
|
||||
/* Do nothing. */ \
|
||||
} else { \
|
||||
MOZ_ASSERT(false, #expr); \
|
||||
} \
|
||||
} while (0)
|
||||
# define MOZ_ALWAYS_FALSE(expr) \
|
||||
do { \
|
||||
if ((expr)) { \
|
||||
MOZ_ASSERT(false, #expr); \
|
||||
} else { \
|
||||
/* Do nothing. */ \
|
||||
} \
|
||||
} while (0)
|
||||
# define MOZ_ALWAYS_OK(expr) MOZ_ASSERT((expr).isOk())
|
||||
# define MOZ_ALWAYS_ERR(expr) MOZ_ASSERT((expr).isErr())
|
||||
#else
|
||||
# define MOZ_ALWAYS_TRUE(expr) \
|
||||
do { \
|
||||
if ((expr)) { \
|
||||
/* Silence MOZ_MUST_USE. */ \
|
||||
} \
|
||||
} while (0)
|
||||
# define MOZ_ALWAYS_FALSE(expr) \
|
||||
do { \
|
||||
if ((expr)) { \
|
||||
/* Silence MOZ_MUST_USE. */ \
|
||||
} \
|
||||
} while (0)
|
||||
# define MOZ_ALWAYS_OK(expr) \
|
||||
do { \
|
||||
if ((expr).isOk()) { \
|
||||
/* Silence MOZ_MUST_USE. */ \
|
||||
} \
|
||||
} while (0)
|
||||
# define MOZ_ALWAYS_ERR(expr) \
|
||||
do { \
|
||||
if ((expr).isErr()) { \
|
||||
/* Silence MOZ_MUST_USE. */ \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#undef MOZ_DUMP_ASSERTION_STACK
|
||||
#undef MOZ_CRASH_CRASHREPORT
|
||||
|
||||
#endif /* mozilla_Assertions_h */
|
||||
|
|
@ -0,0 +1,563 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* Implements (almost always) lock-free atomic operations. The operations here
|
||||
* are a subset of that which can be found in C++11's <atomic> header, with a
|
||||
* different API to enforce consistent memory ordering constraints.
|
||||
*
|
||||
* Anyone caught using |volatile| for inter-thread memory safety needs to be
|
||||
* sent a copy of this header and the C++11 standard.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_Atomics_h
|
||||
#define mozilla_Atomics_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* An enum of memory ordering possibilities for atomics.
|
||||
*
|
||||
* Memory ordering is the observable state of distinct values in memory.
|
||||
* (It's a separate concept from atomicity, which concerns whether an
|
||||
* operation can ever be observed in an intermediate state. Don't
|
||||
* conflate the two!) Given a sequence of operations in source code on
|
||||
* memory, it is *not* always the case that, at all times and on all
|
||||
* cores, those operations will appear to have occurred in that exact
|
||||
* sequence. First, the compiler might reorder that sequence, if it
|
||||
* thinks another ordering will be more efficient. Second, the CPU may
|
||||
* not expose so consistent a view of memory. CPUs will often perform
|
||||
* their own instruction reordering, above and beyond that performed by
|
||||
* the compiler. And each core has its own memory caches, and accesses
|
||||
* (reads and writes both) to "memory" may only resolve to out-of-date
|
||||
* cache entries -- not to the "most recently" performed operation in
|
||||
* some global sense. Any access to a value that may be used by
|
||||
* multiple threads, potentially across multiple cores, must therefore
|
||||
* have a memory ordering imposed on it, for all code on all
|
||||
* threads/cores to have a sufficiently coherent worldview.
|
||||
*
|
||||
* http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync and
|
||||
* http://en.cppreference.com/w/cpp/atomic/memory_order go into more
|
||||
* detail on all this, including examples of how each mode works.
|
||||
*
|
||||
* Note that for simplicity and practicality, not all of the modes in
|
||||
* C++11 are supported. The missing C++11 modes are either subsumed by
|
||||
* the modes we provide below, or not relevant for the CPUs we support
|
||||
* in Gecko. These three modes are confusing enough as it is!
|
||||
*/
|
||||
enum MemoryOrdering {
|
||||
/*
|
||||
* Relaxed ordering is the simplest memory ordering: none at all.
|
||||
* When the result of a write is observed, nothing may be inferred
|
||||
* about other memory. Writes ostensibly performed "before" on the
|
||||
* writing thread may not yet be visible. Writes performed "after" on
|
||||
* the writing thread may already be visible, if the compiler or CPU
|
||||
* reordered them. (The latter can happen if reads and/or writes get
|
||||
* held up in per-processor caches.) Relaxed ordering means
|
||||
* operations can always use cached values (as long as the actual
|
||||
* updates to atomic values actually occur, correctly, eventually), so
|
||||
* it's usually the fastest sort of atomic access. For this reason,
|
||||
* *it's also the most dangerous kind of access*.
|
||||
*
|
||||
* Relaxed ordering is good for things like process-wide statistics
|
||||
* counters that don't need to be consistent with anything else, so
|
||||
* long as updates themselves are atomic. (And so long as any
|
||||
* observations of that value can tolerate being out-of-date -- if you
|
||||
* need some sort of up-to-date value, you need some sort of other
|
||||
* synchronizing operation.) It's *not* good for locks, mutexes,
|
||||
* reference counts, etc. that mediate access to other memory, or must
|
||||
* be observably consistent with other memory.
|
||||
*
|
||||
* x86 architectures don't take advantage of the optimization
|
||||
* opportunities that relaxed ordering permits. Thus it's possible
|
||||
* that using relaxed ordering will "work" on x86 but fail elsewhere
|
||||
* (ARM, say, which *does* implement non-sequentially-consistent
|
||||
* relaxed ordering semantics). Be extra-careful using relaxed
|
||||
* ordering if you can't easily test non-x86 architectures!
|
||||
*/
|
||||
Relaxed,
|
||||
|
||||
/*
|
||||
* When an atomic value is updated with ReleaseAcquire ordering, and
|
||||
* that new value is observed with ReleaseAcquire ordering, prior
|
||||
* writes (atomic or not) are also observable. What ReleaseAcquire
|
||||
* *doesn't* give you is any observable ordering guarantees for
|
||||
* ReleaseAcquire-ordered operations on different objects. For
|
||||
* example, if there are two cores that each perform ReleaseAcquire
|
||||
* operations on separate objects, each core may or may not observe
|
||||
* the operations made by the other core. The only way the cores can
|
||||
* be synchronized with ReleaseAcquire is if they both
|
||||
* ReleaseAcquire-access the same object. This implies that you can't
|
||||
* necessarily describe some global total ordering of ReleaseAcquire
|
||||
* operations.
|
||||
*
|
||||
* ReleaseAcquire ordering is good for (as the name implies) atomic
|
||||
* operations on values controlling ownership of things: reference
|
||||
* counts, mutexes, and the like. However, if you are thinking about
|
||||
* using these to implement your own locks or mutexes, you should take
|
||||
* a good, hard look at actual lock or mutex primitives first.
|
||||
*/
|
||||
ReleaseAcquire,
|
||||
|
||||
/*
|
||||
* When an atomic value is updated with SequentiallyConsistent
|
||||
* ordering, all writes observable when the update is observed, just
|
||||
* as with ReleaseAcquire ordering. But, furthermore, a global total
|
||||
* ordering of SequentiallyConsistent operations *can* be described.
|
||||
* For example, if two cores perform SequentiallyConsistent operations
|
||||
* on separate objects, one core will observably perform its update
|
||||
* (and all previous operations will have completed), then the other
|
||||
* core will observably perform its update (and all previous
|
||||
* operations will have completed). (Although those previous
|
||||
* operations aren't themselves ordered -- they could be intermixed,
|
||||
* or ordered if they occur on atomic values with ordering
|
||||
* requirements.) SequentiallyConsistent is the *simplest and safest*
|
||||
* ordering of atomic operations -- it's always as if one operation
|
||||
* happens, then another, then another, in some order -- and every
|
||||
* core observes updates to happen in that single order. Because it
|
||||
* has the most synchronization requirements, operations ordered this
|
||||
* way also tend to be slowest.
|
||||
*
|
||||
* SequentiallyConsistent ordering can be desirable when multiple
|
||||
* threads observe objects, and they all have to agree on the
|
||||
* observable order of changes to them. People expect
|
||||
* SequentiallyConsistent ordering, even if they shouldn't, when
|
||||
* writing code, atomic or otherwise. SequentiallyConsistent is also
|
||||
* the ordering of choice when designing lockless data structures. If
|
||||
* you don't know what order to use, use this one.
|
||||
*/
|
||||
SequentiallyConsistent,
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* We provide CompareExchangeFailureOrder to work around a bug in some
|
||||
* versions of GCC's <atomic> header. See bug 898491.
|
||||
*/
|
||||
template<MemoryOrdering Order> struct AtomicOrderConstraints;
|
||||
|
||||
template<>
|
||||
struct AtomicOrderConstraints<Relaxed>
|
||||
{
|
||||
static const std::memory_order AtomicRMWOrder = std::memory_order_relaxed;
|
||||
static const std::memory_order LoadOrder = std::memory_order_relaxed;
|
||||
static const std::memory_order StoreOrder = std::memory_order_relaxed;
|
||||
static const std::memory_order CompareExchangeFailureOrder =
|
||||
std::memory_order_relaxed;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AtomicOrderConstraints<ReleaseAcquire>
|
||||
{
|
||||
static const std::memory_order AtomicRMWOrder = std::memory_order_acq_rel;
|
||||
static const std::memory_order LoadOrder = std::memory_order_acquire;
|
||||
static const std::memory_order StoreOrder = std::memory_order_release;
|
||||
static const std::memory_order CompareExchangeFailureOrder =
|
||||
std::memory_order_acquire;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AtomicOrderConstraints<SequentiallyConsistent>
|
||||
{
|
||||
static const std::memory_order AtomicRMWOrder = std::memory_order_seq_cst;
|
||||
static const std::memory_order LoadOrder = std::memory_order_seq_cst;
|
||||
static const std::memory_order StoreOrder = std::memory_order_seq_cst;
|
||||
static const std::memory_order CompareExchangeFailureOrder =
|
||||
std::memory_order_seq_cst;
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicBase
|
||||
{
|
||||
typedef std::atomic<T> ValueType;
|
||||
typedef AtomicOrderConstraints<Order> OrderedOp;
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicMemoryOps : public IntrinsicBase<T, Order>
|
||||
{
|
||||
typedef IntrinsicBase<T, Order> Base;
|
||||
|
||||
static T load(const typename Base::ValueType& aPtr)
|
||||
{
|
||||
return aPtr.load(Base::OrderedOp::LoadOrder);
|
||||
}
|
||||
|
||||
static void store(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
aPtr.store(aVal, Base::OrderedOp::StoreOrder);
|
||||
}
|
||||
|
||||
static T exchange(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
return aPtr.exchange(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static bool compareExchange(typename Base::ValueType& aPtr,
|
||||
T aOldVal, T aNewVal)
|
||||
{
|
||||
return aPtr.compare_exchange_strong(aOldVal, aNewVal,
|
||||
Base::OrderedOp::AtomicRMWOrder,
|
||||
Base::OrderedOp::CompareExchangeFailureOrder);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub : public IntrinsicBase<T, Order>
|
||||
{
|
||||
typedef IntrinsicBase<T, Order> Base;
|
||||
|
||||
static T add(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static T sub(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub<T*, Order> : public IntrinsicBase<T*, Order>
|
||||
{
|
||||
typedef IntrinsicBase<T*, Order> Base;
|
||||
|
||||
static T* add(typename Base::ValueType& aPtr, ptrdiff_t aVal)
|
||||
{
|
||||
return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static T* sub(typename Base::ValueType& aPtr, ptrdiff_t aVal)
|
||||
{
|
||||
return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
|
||||
{
|
||||
typedef IntrinsicBase<T, Order> Base;
|
||||
|
||||
static T inc(typename Base::ValueType& aPtr)
|
||||
{
|
||||
return IntrinsicAddSub<T, Order>::add(aPtr, 1);
|
||||
}
|
||||
|
||||
static T dec(typename Base::ValueType& aPtr)
|
||||
{
|
||||
return IntrinsicAddSub<T, Order>::sub(aPtr, 1);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
|
||||
public IntrinsicIncDec<T, Order>
|
||||
{
|
||||
typedef IntrinsicBase<T, Order> Base;
|
||||
|
||||
static T or_(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
return aPtr.fetch_or(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static T xor_(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
return aPtr.fetch_xor(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static T and_(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
return aPtr.fetch_and(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics<T*, Order>
|
||||
: public IntrinsicMemoryOps<T*, Order>, public IntrinsicIncDec<T*, Order>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ToStorageTypeArgument
|
||||
{
|
||||
static constexpr T convert (T aT) { return aT; }
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class AtomicBase
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8,
|
||||
"mozilla/Atomics.h only supports 32-bit and 64-bit types");
|
||||
|
||||
protected:
|
||||
typedef typename detail::AtomicIntrinsics<T, Order> Intrinsics;
|
||||
typedef typename Intrinsics::ValueType ValueType;
|
||||
ValueType mValue;
|
||||
|
||||
public:
|
||||
constexpr AtomicBase() : mValue() {}
|
||||
explicit constexpr AtomicBase(T aInit)
|
||||
: mValue(ToStorageTypeArgument<T>::convert(aInit))
|
||||
{}
|
||||
|
||||
// Note: we can't provide operator T() here because Atomic<bool> inherits
|
||||
// from AtomcBase with T=uint32_t and not T=bool. If we implemented
|
||||
// operator T() here, it would cause errors when comparing Atomic<bool> with
|
||||
// a regular bool.
|
||||
|
||||
T operator=(T aVal)
|
||||
{
|
||||
Intrinsics::store(mValue, aVal);
|
||||
return aVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an atomic swap operation. aVal is stored and the previous
|
||||
* value of this variable is returned.
|
||||
*/
|
||||
T exchange(T aVal)
|
||||
{
|
||||
return Intrinsics::exchange(mValue, aVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an atomic compare-and-swap operation and returns true if it
|
||||
* succeeded. This is equivalent to atomically doing
|
||||
*
|
||||
* if (mValue == aOldValue) {
|
||||
* mValue = aNewValue;
|
||||
* return true;
|
||||
* } else {
|
||||
* return false;
|
||||
* }
|
||||
*/
|
||||
bool compareExchange(T aOldValue, T aNewValue)
|
||||
{
|
||||
return Intrinsics::compareExchange(mValue, aOldValue, aNewValue);
|
||||
}
|
||||
|
||||
private:
|
||||
template<MemoryOrdering AnyOrder>
|
||||
AtomicBase(const AtomicBase<T, AnyOrder>& aCopy) = delete;
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class AtomicBaseIncDec : public AtomicBase<T, Order>
|
||||
{
|
||||
typedef typename detail::AtomicBase<T, Order> Base;
|
||||
|
||||
public:
|
||||
constexpr AtomicBaseIncDec() : Base() {}
|
||||
explicit constexpr AtomicBaseIncDec(T aInit) : Base(aInit) {}
|
||||
|
||||
using Base::operator=;
|
||||
|
||||
operator T() const { return Base::Intrinsics::load(Base::mValue); }
|
||||
T operator++(int) { return Base::Intrinsics::inc(Base::mValue); }
|
||||
T operator--(int) { return Base::Intrinsics::dec(Base::mValue); }
|
||||
T operator++() { return Base::Intrinsics::inc(Base::mValue) + 1; }
|
||||
T operator--() { return Base::Intrinsics::dec(Base::mValue) - 1; }
|
||||
|
||||
private:
|
||||
template<MemoryOrdering AnyOrder>
|
||||
AtomicBaseIncDec(const AtomicBaseIncDec<T, AnyOrder>& aCopy) = delete;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* A wrapper for a type that enforces that all memory accesses are atomic.
|
||||
*
|
||||
* In general, where a variable |T foo| exists, |Atomic<T> foo| can be used in
|
||||
* its place. Implementations for integral and pointer types are provided
|
||||
* below.
|
||||
*
|
||||
* Atomic accesses are sequentially consistent by default. You should
|
||||
* use the default unless you are tall enough to ride the
|
||||
* memory-ordering roller coaster (if you're not sure, you aren't) and
|
||||
* you have a compelling reason to do otherwise.
|
||||
*
|
||||
* There is one exception to the case of atomic memory accesses: providing an
|
||||
* initial value of the atomic value is not guaranteed to be atomic. This is a
|
||||
* deliberate design choice that enables static atomic variables to be declared
|
||||
* without introducing extra static constructors.
|
||||
*/
|
||||
template<typename T,
|
||||
MemoryOrdering Order = SequentiallyConsistent,
|
||||
typename Enable = void>
|
||||
class Atomic;
|
||||
|
||||
/**
|
||||
* Atomic<T> implementation for integral types.
|
||||
*
|
||||
* In addition to atomic store and load operations, compound assignment and
|
||||
* increment/decrement operators are implemented which perform the
|
||||
* corresponding read-modify-write operation atomically. Finally, an atomic
|
||||
* swap method is provided.
|
||||
*/
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class Atomic<T, Order, typename EnableIf<IsIntegral<T>::value &&
|
||||
!IsSame<T, bool>::value>::Type>
|
||||
: public detail::AtomicBaseIncDec<T, Order>
|
||||
{
|
||||
typedef typename detail::AtomicBaseIncDec<T, Order> Base;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : Base() {}
|
||||
explicit constexpr Atomic(T aInit) : Base(aInit) {}
|
||||
|
||||
using Base::operator=;
|
||||
|
||||
T operator+=(T aDelta)
|
||||
{
|
||||
return Base::Intrinsics::add(Base::mValue, aDelta) + aDelta;
|
||||
}
|
||||
|
||||
T operator-=(T aDelta)
|
||||
{
|
||||
return Base::Intrinsics::sub(Base::mValue, aDelta) - aDelta;
|
||||
}
|
||||
|
||||
T operator|=(T aVal)
|
||||
{
|
||||
return Base::Intrinsics::or_(Base::mValue, aVal) | aVal;
|
||||
}
|
||||
|
||||
T operator^=(T aVal)
|
||||
{
|
||||
return Base::Intrinsics::xor_(Base::mValue, aVal) ^ aVal;
|
||||
}
|
||||
|
||||
T operator&=(T aVal)
|
||||
{
|
||||
return Base::Intrinsics::and_(Base::mValue, aVal) & aVal;
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic(Atomic<T, Order>& aOther) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Atomic<T> implementation for pointer types.
|
||||
*
|
||||
* An atomic compare-and-swap primitive for pointer variables is provided, as
|
||||
* are atomic increment and decement operators. Also provided are the compound
|
||||
* assignment operators for addition and subtraction. Atomic swap (via
|
||||
* exchange()) is included as well.
|
||||
*/
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class Atomic<T*, Order> : public detail::AtomicBaseIncDec<T*, Order>
|
||||
{
|
||||
typedef typename detail::AtomicBaseIncDec<T*, Order> Base;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : Base() {}
|
||||
explicit constexpr Atomic(T* aInit) : Base(aInit) {}
|
||||
|
||||
using Base::operator=;
|
||||
|
||||
T* operator+=(ptrdiff_t aDelta)
|
||||
{
|
||||
return Base::Intrinsics::add(Base::mValue, aDelta) + aDelta;
|
||||
}
|
||||
|
||||
T* operator-=(ptrdiff_t aDelta)
|
||||
{
|
||||
return Base::Intrinsics::sub(Base::mValue, aDelta) - aDelta;
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic(Atomic<T*, Order>& aOther) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Atomic<T> implementation for enum types.
|
||||
*
|
||||
* The atomic store and load operations and the atomic swap method is provided.
|
||||
*/
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class Atomic<T, Order, typename EnableIf<IsEnum<T>::value>::Type>
|
||||
: public detail::AtomicBase<T, Order>
|
||||
{
|
||||
typedef typename detail::AtomicBase<T, Order> Base;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : Base() {}
|
||||
explicit constexpr Atomic(T aInit) : Base(aInit) {}
|
||||
|
||||
operator T() const { return T(Base::Intrinsics::load(Base::mValue)); }
|
||||
|
||||
using Base::operator=;
|
||||
|
||||
private:
|
||||
Atomic(Atomic<T, Order>& aOther) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Atomic<T> implementation for boolean types.
|
||||
*
|
||||
* The atomic store and load operations and the atomic swap method is provided.
|
||||
*
|
||||
* Note:
|
||||
*
|
||||
* - sizeof(Atomic<bool>) != sizeof(bool) for some implementations of
|
||||
* bool and/or some implementations of std::atomic. This is allowed in
|
||||
* [atomic.types.generic]p9.
|
||||
*
|
||||
* - It's not obvious whether the 8-bit atomic functions on Windows are always
|
||||
* inlined or not. If they are not inlined, the corresponding functions in the
|
||||
* runtime library are not available on Windows XP. This is why we implement
|
||||
* Atomic<bool> with an underlying type of uint32_t.
|
||||
*/
|
||||
template<MemoryOrdering Order>
|
||||
class Atomic<bool, Order>
|
||||
: protected detail::AtomicBase<uint32_t, Order>
|
||||
{
|
||||
typedef typename detail::AtomicBase<uint32_t, Order> Base;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : Base() {}
|
||||
explicit constexpr Atomic(bool aInit) : Base(aInit) {}
|
||||
|
||||
// We provide boolean wrappers for the underlying AtomicBase methods.
|
||||
MOZ_IMPLICIT operator bool() const
|
||||
{
|
||||
return Base::Intrinsics::load(Base::mValue);
|
||||
}
|
||||
|
||||
bool operator=(bool aVal)
|
||||
{
|
||||
return Base::operator=(aVal);
|
||||
}
|
||||
|
||||
bool exchange(bool aVal)
|
||||
{
|
||||
return Base::exchange(aVal);
|
||||
}
|
||||
|
||||
bool compareExchange(bool aOldValue, bool aNewValue)
|
||||
{
|
||||
return Base::compareExchange(aOldValue, aNewValue);
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic(Atomic<bool, Order>& aOther) = delete;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_Atomics_h */
|
||||
|
|
@ -0,0 +1,747 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Implementations of various class and method modifier attributes. */
|
||||
|
||||
#ifndef mozilla_Attributes_h
|
||||
#define mozilla_Attributes_h
|
||||
|
||||
#include "mozilla/Compiler.h"
|
||||
|
||||
/*
|
||||
* MOZ_ALWAYS_INLINE is a macro which expands to tell the compiler that the
|
||||
* method decorated with it must be inlined, even if the compiler thinks
|
||||
* otherwise. This is only a (much) stronger version of the inline hint:
|
||||
* compilers are not guaranteed to respect it (although they're much more likely
|
||||
* to do so).
|
||||
*
|
||||
* The MOZ_ALWAYS_INLINE_EVEN_DEBUG macro is yet stronger. It tells the
|
||||
* compiler to inline even in DEBUG builds. It should be used very rarely.
|
||||
*/
|
||||
#if defined(_MSC_VER)
|
||||
# define MOZ_ALWAYS_INLINE_EVEN_DEBUG __forceinline
|
||||
#elif defined(__GNUC__)
|
||||
# define MOZ_ALWAYS_INLINE_EVEN_DEBUG __attribute__((always_inline)) inline
|
||||
#else
|
||||
# define MOZ_ALWAYS_INLINE_EVEN_DEBUG inline
|
||||
#endif
|
||||
|
||||
#if !defined(DEBUG)
|
||||
# define MOZ_ALWAYS_INLINE MOZ_ALWAYS_INLINE_EVEN_DEBUG
|
||||
#elif defined(_MSC_VER) && !defined(__cplusplus)
|
||||
# define MOZ_ALWAYS_INLINE __inline
|
||||
#else
|
||||
# define MOZ_ALWAYS_INLINE inline
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
/*
|
||||
* g++ requires -std=c++0x or -std=gnu++0x to support C++11 functionality
|
||||
* without warnings (functionality used by the macros below). These modes are
|
||||
* detectable by checking whether __GXX_EXPERIMENTAL_CXX0X__ is defined or, more
|
||||
* standardly, by checking whether __cplusplus has a C++11 or greater value.
|
||||
* Current versions of g++ do not correctly set __cplusplus, so we check both
|
||||
* for forward compatibility.
|
||||
*/
|
||||
# define MOZ_HAVE_NEVER_INLINE __declspec(noinline)
|
||||
# define MOZ_HAVE_NORETURN __declspec(noreturn)
|
||||
#elif defined(__clang__)
|
||||
/*
|
||||
* Per Clang documentation, "Note that marketing version numbers should not
|
||||
* be used to check for language features, as different vendors use different
|
||||
* numbering schemes. Instead, use the feature checking macros."
|
||||
*/
|
||||
# ifndef __has_extension
|
||||
# define __has_extension __has_feature /* compatibility, for older versions of clang */
|
||||
# endif
|
||||
# if __has_attribute(noinline)
|
||||
# define MOZ_HAVE_NEVER_INLINE __attribute__((noinline))
|
||||
# endif
|
||||
# if __has_attribute(noreturn)
|
||||
# define MOZ_HAVE_NORETURN __attribute__((noreturn))
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
# define MOZ_HAVE_NEVER_INLINE __attribute__((noinline))
|
||||
# define MOZ_HAVE_NORETURN __attribute__((noreturn))
|
||||
# define MOZ_HAVE_NORETURN_PTR __attribute__((noreturn))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When built with clang analyzer (a.k.a scan-build), define MOZ_HAVE_NORETURN
|
||||
* to mark some false positives
|
||||
*/
|
||||
#ifdef __clang_analyzer__
|
||||
# if __has_extension(attribute_analyzer_noreturn)
|
||||
# define MOZ_HAVE_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_NEVER_INLINE is a macro which expands to tell the compiler that the
|
||||
* method decorated with it must never be inlined, even if the compiler would
|
||||
* otherwise choose to inline the method. Compilers aren't absolutely
|
||||
* guaranteed to support this, but most do.
|
||||
*/
|
||||
#if defined(MOZ_HAVE_NEVER_INLINE)
|
||||
# define MOZ_NEVER_INLINE MOZ_HAVE_NEVER_INLINE
|
||||
#else
|
||||
# define MOZ_NEVER_INLINE /* no support */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_NORETURN, specified at the start of a function declaration, indicates
|
||||
* that the given function does not return. (The function definition does not
|
||||
* need to be annotated.)
|
||||
*
|
||||
* MOZ_NORETURN void abort(const char* msg);
|
||||
*
|
||||
* This modifier permits the compiler to optimize code assuming a call to such a
|
||||
* function will never return. It also enables the compiler to avoid spurious
|
||||
* warnings about not initializing variables, or about any other seemingly-dodgy
|
||||
* operations performed after the function returns.
|
||||
*
|
||||
* There are two variants. The GCC version of NORETURN may be applied to a
|
||||
* function pointer, while for MSVC it may not.
|
||||
*
|
||||
* This modifier does not affect the corresponding function's linking behavior.
|
||||
*/
|
||||
#if defined(MOZ_HAVE_NORETURN)
|
||||
# define MOZ_NORETURN MOZ_HAVE_NORETURN
|
||||
#else
|
||||
# define MOZ_NORETURN /* no support */
|
||||
#endif
|
||||
#if defined(MOZ_HAVE_NORETURN_PTR)
|
||||
# define MOZ_NORETURN_PTR MOZ_HAVE_NORETURN_PTR
|
||||
#else
|
||||
# define MOZ_NORETURN_PTR /* no support */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* MOZ_COLD tells the compiler that a function is "cold", meaning infrequently
|
||||
* executed. This may lead it to optimize for size more aggressively than speed,
|
||||
* or to allocate the body of the function in a distant part of the text segment
|
||||
* to help keep it from taking up unnecessary icache when it isn't in use.
|
||||
*
|
||||
* Place this attribute at the very beginning of a function definition. For
|
||||
* example, write
|
||||
*
|
||||
* MOZ_COLD int foo();
|
||||
*
|
||||
* or
|
||||
*
|
||||
* MOZ_COLD int foo() { return 42; }
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define MOZ_COLD __attribute__ ((cold))
|
||||
#else
|
||||
# define MOZ_COLD
|
||||
#endif
|
||||
|
||||
/**
|
||||
* MOZ_NONNULL tells the compiler that some of the arguments to a function are
|
||||
* known to be non-null. The arguments are a list of 1-based argument indexes
|
||||
* identifying arguments which are known to be non-null.
|
||||
*
|
||||
* Place this attribute at the very beginning of a function definition. For
|
||||
* example, write
|
||||
*
|
||||
* MOZ_NONNULL(1, 2) int foo(char *p, char *q);
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define MOZ_NONNULL(...) __attribute__ ((nonnull(__VA_ARGS__)))
|
||||
#else
|
||||
# define MOZ_NONNULL(...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* MOZ_NONNULL_RETURN tells the compiler that the function's return value is
|
||||
* guaranteed to be a non-null pointer, which may enable the compiler to
|
||||
* optimize better at call sites.
|
||||
*
|
||||
* Place this attribute at the end of a function declaration. For example,
|
||||
*
|
||||
* char* foo(char *p, char *q) MOZ_NONNULL_RETURN;
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define MOZ_NONNULL_RETURN __attribute__ ((returns_nonnull))
|
||||
#else
|
||||
# define MOZ_NONNULL_RETURN
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS, specified at the end of a function
|
||||
* declaration, indicates that for the purposes of static analysis, this
|
||||
* function does not return. (The function definition does not need to be
|
||||
* annotated.)
|
||||
*
|
||||
* MOZ_ReportCrash(const char* s, const char* file, int ln)
|
||||
* MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS
|
||||
*
|
||||
* Some static analyzers, like scan-build from clang, can use this information
|
||||
* to eliminate false positives. From the upstream documentation of scan-build:
|
||||
* "This attribute is useful for annotating assertion handlers that actually
|
||||
* can return, but for the purpose of using the analyzer we want to pretend
|
||||
* that such functions do not return."
|
||||
*
|
||||
*/
|
||||
#if defined(MOZ_HAVE_ANALYZER_NORETURN)
|
||||
# define MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS MOZ_HAVE_ANALYZER_NORETURN
|
||||
#else
|
||||
# define MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS /* no support */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_ASAN_BLACKLIST is a macro to tell AddressSanitizer (a compile-time
|
||||
* instrumentation shipped with Clang and GCC) to not instrument the annotated
|
||||
* function. Furthermore, it will prevent the compiler from inlining the
|
||||
* function because inlining currently breaks the blacklisting mechanism of
|
||||
* AddressSanitizer.
|
||||
*/
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
# define MOZ_HAVE_ASAN_BLACKLIST
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
# if defined(__SANITIZE_ADDRESS__)
|
||||
# define MOZ_HAVE_ASAN_BLACKLIST
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_HAVE_ASAN_BLACKLIST)
|
||||
# define MOZ_ASAN_BLACKLIST MOZ_NEVER_INLINE __attribute__((no_sanitize_address))
|
||||
#else
|
||||
# define MOZ_ASAN_BLACKLIST /* nothing */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_TSAN_BLACKLIST is a macro to tell ThreadSanitizer (a compile-time
|
||||
* instrumentation shipped with Clang) to not instrument the annotated function.
|
||||
* Furthermore, it will prevent the compiler from inlining the function because
|
||||
* inlining currently breaks the blacklisting mechanism of ThreadSanitizer.
|
||||
*/
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(thread_sanitizer)
|
||||
# define MOZ_TSAN_BLACKLIST MOZ_NEVER_INLINE __attribute__((no_sanitize_thread))
|
||||
# else
|
||||
# define MOZ_TSAN_BLACKLIST /* nothing */
|
||||
# endif
|
||||
#else
|
||||
# define MOZ_TSAN_BLACKLIST /* nothing */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The MOZ_NO_SANITIZE_* family of macros is an annotation based on a more recently
|
||||
* introduced Clang feature that allows disabling various sanitizer features for
|
||||
* the particular function, including those from UndefinedBehaviorSanitizer.
|
||||
*/
|
||||
|
||||
#if defined(__has_attribute)
|
||||
# if __has_attribute(no_sanitize)
|
||||
# define MOZ_HAVE_NO_SANITIZE_ATTR
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_HAVE_NO_SANITIZE_ATTR)
|
||||
# define MOZ_NO_SANITIZE_UINT_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
|
||||
# define MOZ_NO_SANITIZE_INT_OVERFLOW __attribute__((no_sanitize("signed-integer-overflow")))
|
||||
#else
|
||||
# define MOZ_NO_SANITIZE_UINT_OVERFLOW /* nothing */
|
||||
# define MOZ_NO_SANITIZE_INT_OVERFLOW /* nothing */
|
||||
#endif
|
||||
|
||||
|
||||
#undef MOZ_HAVE_NO_SANITIZE_ATTR
|
||||
|
||||
|
||||
/**
|
||||
* MOZ_ALLOCATOR tells the compiler that the function it marks returns either a
|
||||
* "fresh", "pointer-free" block of memory, or nullptr. "Fresh" means that the
|
||||
* block is not pointed to by any other reachable pointer in the program.
|
||||
* "Pointer-free" means that the block contains no pointers to any valid object
|
||||
* in the program. It may be initialized with other (non-pointer) values.
|
||||
*
|
||||
* Placing this attribute on appropriate functions helps GCC analyze pointer
|
||||
* aliasing more accurately in their callers.
|
||||
*
|
||||
* GCC warns if a caller ignores the value returned by a function marked with
|
||||
* MOZ_ALLOCATOR: it is hard to imagine cases where dropping the value returned
|
||||
* by a function that meets the criteria above would be intentional.
|
||||
*
|
||||
* Place this attribute after the argument list and 'this' qualifiers of a
|
||||
* function definition. For example, write
|
||||
*
|
||||
* void *my_allocator(size_t) MOZ_ALLOCATOR;
|
||||
*
|
||||
* or
|
||||
*
|
||||
* void *my_allocator(size_t bytes) MOZ_ALLOCATOR { ... }
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define MOZ_ALLOCATOR __attribute__ ((malloc, warn_unused_result))
|
||||
#else
|
||||
# define MOZ_ALLOCATOR
|
||||
#endif
|
||||
|
||||
/**
|
||||
* MOZ_MUST_USE tells the compiler to emit a warning if a function's
|
||||
* return value is not used by the caller.
|
||||
*
|
||||
* Place this attribute at the very beginning of a function declaration. For
|
||||
* example, write
|
||||
*
|
||||
* MOZ_MUST_USE int foo();
|
||||
* or
|
||||
* MOZ_MUST_USE int foo() { return 42; }
|
||||
*
|
||||
* MOZ_MUST_USE is most appropriate for functions where the return value is
|
||||
* some kind of success/failure indicator -- often |nsresult|, |bool| or |int|
|
||||
* -- because these functions are most commonly the ones that have missing
|
||||
* checks. There are three cases of note.
|
||||
*
|
||||
* - Fallible functions whose return values should always be checked. For
|
||||
* example, a function that opens a file should always be checked because any
|
||||
* subsequent operations on the file will fail if opening it fails. Such
|
||||
* functions should be given a MOZ_MUST_USE annotation.
|
||||
*
|
||||
* - Fallible functions whose return value need not always be checked. For
|
||||
* example, a function that closes a file might not be checked because it's
|
||||
* common that no further operations would be performed on the file. Such
|
||||
* functions do not need a MOZ_MUST_USE annotation.
|
||||
*
|
||||
* - Infallible functions, i.e. ones that always return a value indicating
|
||||
* success. These do not need a MOZ_MUST_USE annotation. Ideally, they would
|
||||
* be converted to not return a success/failure indicator, though sometimes
|
||||
* interface constraints prevent this.
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define MOZ_MUST_USE __attribute__ ((warn_unused_result))
|
||||
#else
|
||||
# define MOZ_MUST_USE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* MOZ_MAYBE_UNUSED suppresses compiler warnings about functions that are
|
||||
* never called (in this build configuration, at least).
|
||||
*
|
||||
* Place this attribute at the very beginning of a function declaration. For
|
||||
* example, write
|
||||
*
|
||||
* MOZ_MAYBE_UNUSED int foo();
|
||||
*
|
||||
* or
|
||||
*
|
||||
* MOZ_MAYBE_UNUSED int foo() { return 42; }
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define MOZ_MAYBE_UNUSED __attribute__ ((__unused__))
|
||||
#elif defined(_MSC_VER)
|
||||
# define MOZ_MAYBE_UNUSED __pragma(warning(suppress:4505))
|
||||
#else
|
||||
# define MOZ_MAYBE_UNUSED
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/**
|
||||
* MOZ_FALLTHROUGH is an annotation to suppress compiler warnings about switch
|
||||
* cases that fall through without a break or return statement. MOZ_FALLTHROUGH
|
||||
* is only needed on cases that have code.
|
||||
*
|
||||
* MOZ_FALLTHROUGH_ASSERT is an annotation to suppress compiler warnings about
|
||||
* switch cases that MOZ_ASSERT(false) (or its alias MOZ_ASSERT_UNREACHABLE) in
|
||||
* debug builds, but intentionally fall through in release builds. See comment
|
||||
* in Assertions.h for more details.
|
||||
*
|
||||
* switch (foo) {
|
||||
* case 1: // These cases have no code. No fallthrough annotations are needed.
|
||||
* case 2:
|
||||
* case 3: // This case has code, so a fallthrough annotation is needed!
|
||||
* foo++;
|
||||
* MOZ_FALLTHROUGH;
|
||||
* case 4:
|
||||
* return foo;
|
||||
*
|
||||
* default:
|
||||
* // This case asserts in debug builds, falls through in release.
|
||||
* MOZ_FALLTHROUGH_ASSERT("Unexpected foo value?!");
|
||||
* case 5:
|
||||
* return 5;
|
||||
* }
|
||||
*/
|
||||
#ifndef __has_cpp_attribute
|
||||
# define __has_cpp_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_cpp_attribute(clang::fallthrough)
|
||||
# define MOZ_FALLTHROUGH [[clang::fallthrough]]
|
||||
#elif __has_cpp_attribute(gnu::fallthrough)
|
||||
# define MOZ_FALLTHROUGH [[gnu::fallthrough]]
|
||||
#elif defined(_MSC_VER)
|
||||
/*
|
||||
* MSVC's __fallthrough annotations are checked by /analyze (Code Analysis):
|
||||
* https://msdn.microsoft.com/en-us/library/ms235402%28VS.80%29.aspx
|
||||
*/
|
||||
# include <sal.h>
|
||||
# define MOZ_FALLTHROUGH __fallthrough
|
||||
#else
|
||||
# define MOZ_FALLTHROUGH /* FALLTHROUGH */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following macros are attributes that support the static analysis plugin
|
||||
* included with Mozilla, and will be implemented (when such support is enabled)
|
||||
* as C++11 attributes. Since such attributes are legal pretty much everywhere
|
||||
* and have subtly different semantics depending on their placement, the
|
||||
* following is a guide on where to place the attributes.
|
||||
*
|
||||
* Attributes that apply to a struct or class precede the name of the class:
|
||||
* (Note that this is different from the placement of final for classes!)
|
||||
*
|
||||
* class MOZ_CLASS_ATTRIBUTE SomeClass {};
|
||||
*
|
||||
* Attributes that apply to functions follow the parentheses and const
|
||||
* qualifiers but precede final, override and the function body:
|
||||
*
|
||||
* void DeclaredFunction() MOZ_FUNCTION_ATTRIBUTE;
|
||||
* void SomeFunction() MOZ_FUNCTION_ATTRIBUTE {}
|
||||
* void PureFunction() const MOZ_FUNCTION_ATTRIBUTE = 0;
|
||||
* void OverriddenFunction() MOZ_FUNCTION_ATTIRBUTE override;
|
||||
*
|
||||
* Attributes that apply to variables or parameters follow the variable's name:
|
||||
*
|
||||
* int variable MOZ_VARIABLE_ATTRIBUTE;
|
||||
*
|
||||
* Attributes that apply to types follow the type name:
|
||||
*
|
||||
* typedef int MOZ_TYPE_ATTRIBUTE MagicInt;
|
||||
* int MOZ_TYPE_ATTRIBUTE someVariable;
|
||||
* int* MOZ_TYPE_ATTRIBUTE magicPtrInt;
|
||||
* int MOZ_TYPE_ATTRIBUTE* ptrToMagicInt;
|
||||
*
|
||||
* Attributes that apply to statements precede the statement:
|
||||
*
|
||||
* MOZ_IF_ATTRIBUTE if (x == 0)
|
||||
* MOZ_DO_ATTRIBUTE do { } while (0);
|
||||
*
|
||||
* Attributes that apply to labels precede the label:
|
||||
*
|
||||
* MOZ_LABEL_ATTRIBUTE target:
|
||||
* goto target;
|
||||
* MOZ_CASE_ATTRIBUTE case 5:
|
||||
* MOZ_DEFAULT_ATTRIBUTE default:
|
||||
*
|
||||
* The static analyses that are performed by the plugin are as follows:
|
||||
*
|
||||
* MOZ_CAN_RUN_SCRIPT: Applies to functions which can run script. Callers of
|
||||
* this function must also be marked as MOZ_CAN_RUN_SCRIPT, and all refcounted
|
||||
* arguments must be strongly held in the caller.
|
||||
* MOZ_MUST_OVERRIDE: Applies to all C++ member functions. All immediate
|
||||
* subclasses must provide an exact override of this method; if a subclass
|
||||
* does not override this method, the compiler will emit an error. This
|
||||
* attribute is not limited to virtual methods, so if it is applied to a
|
||||
* nonvirtual method and the subclass does not provide an equivalent
|
||||
* definition, the compiler will emit an error.
|
||||
* MOZ_STACK_CLASS: Applies to all classes. Any class with this annotation is
|
||||
* expected to live on the stack, so it is a compile-time error to use it, or
|
||||
* an array of such objects, as a global or static variable, or as the type of
|
||||
* a new expression (unless placement new is being used). If a member of
|
||||
* another class uses this class, or if another class inherits from this
|
||||
* class, then it is considered to be a stack class as well, although this
|
||||
* attribute need not be provided in such cases.
|
||||
* MOZ_NONHEAP_CLASS: Applies to all classes. Any class with this annotation is
|
||||
* expected to live on the stack or in static storage, so it is a compile-time
|
||||
* error to use it, or an array of such objects, as the type of a new
|
||||
* expression. If a member of another class uses this class, or if another
|
||||
* class inherits from this class, then it is considered to be a non-heap class
|
||||
* as well, although this attribute need not be provided in such cases.
|
||||
* MOZ_HEAP_CLASS: Applies to all classes. Any class with this annotation is
|
||||
* expected to live on the heap, so it is a compile-time error to use it, or
|
||||
* an array of such objects, as the type of a variable declaration, or as a
|
||||
* temporary object. If a member of another class uses this class, or if
|
||||
* another class inherits from this class, then it is considered to be a heap
|
||||
* class as well, although this attribute need not be provided in such cases.
|
||||
* MOZ_NON_TEMPORARY_CLASS: Applies to all classes. Any class with this
|
||||
* annotation is expected not to live in a temporary. If a member of another
|
||||
* class uses this class or if another class inherits from this class, then it
|
||||
* is considered to be a non-temporary class as well, although this attribute
|
||||
* need not be provided in such cases.
|
||||
* MOZ_RAII: Applies to all classes. Any class with this annotation is assumed
|
||||
* to be a RAII guard, which is expected to live on the stack in an automatic
|
||||
* allocation. It is prohibited from being allocated in a temporary, static
|
||||
* storage, or on the heap. This is a combination of MOZ_STACK_CLASS and
|
||||
* MOZ_NON_TEMPORARY_CLASS.
|
||||
* MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS: Applies to all classes that are
|
||||
* intended to prevent introducing static initializers. This attribute
|
||||
* currently makes it a compile-time error to instantiate these classes
|
||||
* anywhere other than at the global scope, or as a static member of a class.
|
||||
* In non-debug mode, it also prohibits non-trivial constructors and
|
||||
* destructors.
|
||||
* MOZ_TRIVIAL_CTOR_DTOR: Applies to all classes that must have both a trivial
|
||||
* or constexpr constructor and a trivial destructor. Setting this attribute
|
||||
* on a class makes it a compile-time error for that class to get a
|
||||
* non-trivial constructor or destructor for any reason.
|
||||
* MOZ_HEAP_ALLOCATOR: Applies to any function. This indicates that the return
|
||||
* value is allocated on the heap, and will as a result check such allocations
|
||||
* during MOZ_STACK_CLASS and MOZ_NONHEAP_CLASS annotation checking.
|
||||
* MOZ_IMPLICIT: Applies to constructors. Implicit conversion constructors
|
||||
* are disallowed by default unless they are marked as MOZ_IMPLICIT. This
|
||||
* attribute must be used for constructors which intend to provide implicit
|
||||
* conversions.
|
||||
* MOZ_IS_REFPTR: Applies to class declarations of ref pointer to mark them as
|
||||
* such for use with static-analysis.
|
||||
* A ref pointer is an object wrapping a pointer and automatically taking care
|
||||
* of its refcounting upon construction/destruction/transfer of ownership.
|
||||
* This annotation implies MOZ_IS_SMARTPTR_TO_REFCOUNTED.
|
||||
* MOZ_IS_SMARTPTR_TO_REFCOUNTED: Applies to class declarations of smart
|
||||
* pointers to ref counted classes to mark them as such for use with
|
||||
* static-analysis.
|
||||
* MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT: Applies to functions. Makes it a compile
|
||||
* time error to pass arithmetic expressions on variables to the function.
|
||||
* MOZ_OWNING_REF: Applies to declarations of pointers to reference counted
|
||||
* types. This attribute tells the compiler that the raw pointer is a strong
|
||||
* reference, where ownership through methods such as AddRef and Release is
|
||||
* managed manually. This can make the compiler ignore these pointers when
|
||||
* validating the usage of pointers otherwise.
|
||||
*
|
||||
* Example uses include owned pointers inside of unions, and pointers stored
|
||||
* in POD types where a using a smart pointer class would make the object
|
||||
* non-POD.
|
||||
* MOZ_NON_OWNING_REF: Applies to declarations of pointers to reference counted
|
||||
* types. This attribute tells the compiler that the raw pointer is a weak
|
||||
* reference, which is ensured to be valid by a guarantee that the reference
|
||||
* will be nulled before the pointer becomes invalid. This can make the compiler
|
||||
* ignore these pointers when validating the usage of pointers otherwise.
|
||||
*
|
||||
* Examples include an mOwner pointer, which is nulled by the owning class's
|
||||
* destructor, and is null-checked before dereferencing.
|
||||
* MOZ_UNSAFE_REF: Applies to declarations of pointers to reference counted types.
|
||||
* Occasionally there are non-owning references which are valid, but do not take
|
||||
* the form of a MOZ_NON_OWNING_REF. Their safety may be dependent on the behaviour
|
||||
* of API consumers. The string argument passed to this macro documents the safety
|
||||
* conditions. This can make the compiler ignore these pointers when validating
|
||||
* the usage of pointers elsewhere.
|
||||
*
|
||||
* Examples include an nsIAtom* member which is known at compile time to point to a
|
||||
* static atom which is valid throughout the lifetime of the program, or an API which
|
||||
* stores a pointer, but doesn't take ownership over it, instead requiring the API
|
||||
* consumer to correctly null the value before it becomes invalid.
|
||||
*
|
||||
* Use of this annotation is discouraged when a strong reference or one of the above
|
||||
* two annotations can be used instead.
|
||||
* MOZ_NO_ADDREF_RELEASE_ON_RETURN: Applies to function declarations. Makes it
|
||||
* a compile time error to call AddRef or Release on the return value of a
|
||||
* function. This is intended to be used with operator->() of our smart
|
||||
* pointer classes to ensure that the refcount of an object wrapped in a
|
||||
* smart pointer is not manipulated directly.
|
||||
* MOZ_MUST_USE_TYPE: Applies to type declarations. Makes it a compile time
|
||||
* error to not use the return value of a function which has this type. This
|
||||
* is intended to be used with types which it is an error to not use.
|
||||
* MOZ_NEEDS_NO_VTABLE_TYPE: Applies to template class declarations. Makes it
|
||||
* a compile time error to instantiate this template with a type parameter which
|
||||
* has a VTable.
|
||||
* MOZ_NON_MEMMOVABLE: Applies to class declarations for types that are not safe
|
||||
* to be moved in memory using memmove().
|
||||
* MOZ_NEEDS_MEMMOVABLE_TYPE: Applies to template class declarations where the
|
||||
* template arguments are required to be safe to move in memory using
|
||||
* memmove(). Passing MOZ_NON_MEMMOVABLE types to these templates is a
|
||||
* compile time error.
|
||||
* MOZ_NEEDS_MEMMOVABLE_MEMBERS: Applies to class declarations where each member
|
||||
* must be safe to move in memory using memmove(). MOZ_NON_MEMMOVABLE types
|
||||
* used in members of these classes are compile time errors.
|
||||
* MOZ_NO_DANGLING_ON_TEMPORARIES: Applies to method declarations which return
|
||||
* a pointer that is freed when the destructor of the class is called. This
|
||||
* prevents these methods from being called on temporaries of the class,
|
||||
* reducing risks of use-after-free.
|
||||
* This attribute cannot be applied to && methods.
|
||||
* In some cases, adding a deleted &&-qualified overload is too restrictive as
|
||||
* this method should still be callable as a non-escaping argument to another
|
||||
* function. This annotation can be used in those cases.
|
||||
* MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS: Applies to template class
|
||||
* declarations where an instance of the template should be considered, for
|
||||
* static analysis purposes, to inherit any type annotations (such as
|
||||
* MOZ_MUST_USE_TYPE and MOZ_STACK_CLASS) from its template arguments.
|
||||
* MOZ_INIT_OUTSIDE_CTOR: Applies to class member declarations. Occasionally
|
||||
* there are class members that are not initialized in the constructor,
|
||||
* but logic elsewhere in the class ensures they are initialized prior to use.
|
||||
* Using this attribute on a member disables the check that this member must be
|
||||
* initialized in constructors via list-initialization, in the constructor body,
|
||||
* or via functions called from the constructor body.
|
||||
* MOZ_IS_CLASS_INIT: Applies to class method declarations. Occasionally the
|
||||
* constructor doesn't initialize all of the member variables and another function
|
||||
* is used to initialize the rest. This marker is used to make the static analysis
|
||||
* tool aware that the marked function is part of the initialization process
|
||||
* and to include the marked function in the scan mechanism that determines witch
|
||||
* member variables still remain uninitialized.
|
||||
* MOZ_NON_PARAM: Applies to types. Makes it compile time error to use the type
|
||||
* in parameter without pointer or reference.
|
||||
* MOZ_NON_AUTOABLE: Applies to class declarations. Makes it a compile time error to
|
||||
* use `auto` in place of this type in variable declarations. This is intended to
|
||||
* be used with types which are intended to be implicitly constructed into other
|
||||
* other types before being assigned to variables.
|
||||
* MOZ_REQUIRED_BASE_METHOD: Applies to virtual class method declarations.
|
||||
* Sometimes derived classes override methods that need to be called by their
|
||||
* overridden counterparts. This marker indicates that the marked method must
|
||||
* be called by the method that it overrides.
|
||||
* MOZ_MUST_RETURN_FROM_CALLER: Applies to function or method declarations.
|
||||
* Callers of the annotated function/method must return from that function
|
||||
* within the calling block using an explicit `return` statement.
|
||||
* Only calls to Constructors, references to local and member variables,
|
||||
* and calls to functions or methods marked as MOZ_MAY_CALL_AFTER_MUST_RETURN
|
||||
* may be made after the MUST_RETURN_FROM_CALLER call.
|
||||
* MOZ_MAY_CALL_AFTER_MUST_RETURN: Applies to function or method declarations.
|
||||
* Calls to these methods may be made in functions after calls a
|
||||
* MOZ_MUST_RETURN_FROM_CALLER function or method.
|
||||
*/
|
||||
#ifdef MOZ_CLANG_PLUGIN
|
||||
# define MOZ_CAN_RUN_SCRIPT __attribute__((annotate("moz_can_run_script")))
|
||||
# define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
|
||||
# define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
|
||||
# define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
|
||||
# define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class")))
|
||||
# define MOZ_NON_TEMPORARY_CLASS __attribute__((annotate("moz_non_temporary_class")))
|
||||
# define MOZ_TRIVIAL_CTOR_DTOR __attribute__((annotate("moz_trivial_ctor_dtor")))
|
||||
# ifdef DEBUG
|
||||
/* in debug builds, these classes do have non-trivial constructors. */
|
||||
# define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class")))
|
||||
# else
|
||||
# define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class"))) \
|
||||
MOZ_TRIVIAL_CTOR_DTOR
|
||||
# endif
|
||||
# define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
|
||||
# define MOZ_IS_SMARTPTR_TO_REFCOUNTED __attribute__((annotate("moz_is_smartptr_to_refcounted")))
|
||||
# define MOZ_IS_REFPTR __attribute__((annotate("moz_is_refptr"))) \
|
||||
MOZ_IS_SMARTPTR_TO_REFCOUNTED
|
||||
# define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT __attribute__((annotate("moz_no_arith_expr_in_arg")))
|
||||
# define MOZ_OWNING_REF __attribute__((annotate("moz_strong_ref")))
|
||||
# define MOZ_NON_OWNING_REF __attribute__((annotate("moz_weak_ref")))
|
||||
# define MOZ_UNSAFE_REF(reason) __attribute__((annotate("moz_weak_ref")))
|
||||
# define MOZ_NO_ADDREF_RELEASE_ON_RETURN __attribute__((annotate("moz_no_addref_release_on_return")))
|
||||
# define MOZ_MUST_USE_TYPE __attribute__((annotate("moz_must_use_type")))
|
||||
# define MOZ_NEEDS_NO_VTABLE_TYPE __attribute__((annotate("moz_needs_no_vtable_type")))
|
||||
# define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable")))
|
||||
# define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type")))
|
||||
# define MOZ_NEEDS_MEMMOVABLE_MEMBERS __attribute__((annotate("moz_needs_memmovable_members")))
|
||||
# define MOZ_NO_DANGLING_ON_TEMPORARIES __attribute__((annotate("moz_no_dangling_on_temporaries")))
|
||||
# define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS \
|
||||
__attribute__((annotate("moz_inherit_type_annotations_from_template_args")))
|
||||
# define MOZ_NON_AUTOABLE __attribute__((annotate("moz_non_autoable")))
|
||||
# define MOZ_INIT_OUTSIDE_CTOR \
|
||||
__attribute__((annotate("moz_ignore_ctor_initialization")))
|
||||
# define MOZ_IS_CLASS_INIT \
|
||||
__attribute__((annotate("moz_is_class_init")))
|
||||
# define MOZ_NON_PARAM \
|
||||
__attribute__((annotate("moz_non_param")))
|
||||
# define MOZ_REQUIRED_BASE_METHOD \
|
||||
__attribute__((annotate("moz_required_base_method")))
|
||||
# define MOZ_MUST_RETURN_FROM_CALLER \
|
||||
__attribute__((annotate("moz_must_return_from_caller")))
|
||||
# define MOZ_MAY_CALL_AFTER_MUST_RETURN \
|
||||
__attribute__((annotate("moz_may_call_after_must_return")))
|
||||
/*
|
||||
* It turns out that clang doesn't like void func() __attribute__ {} without a
|
||||
* warning, so use pragmas to disable the warning. This code won't work on GCC
|
||||
* anyways, so the warning is safe to ignore.
|
||||
*/
|
||||
# define MOZ_HEAP_ALLOCATOR \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
|
||||
__attribute__((annotate("moz_heap_allocator"))) \
|
||||
_Pragma("clang diagnostic pop")
|
||||
#else
|
||||
# define MOZ_CAN_RUN_SCRIPT /* nothing */
|
||||
# define MOZ_MUST_OVERRIDE /* nothing */
|
||||
# define MOZ_STACK_CLASS /* nothing */
|
||||
# define MOZ_NONHEAP_CLASS /* nothing */
|
||||
# define MOZ_HEAP_CLASS /* nothing */
|
||||
# define MOZ_NON_TEMPORARY_CLASS /* nothing */
|
||||
# define MOZ_TRIVIAL_CTOR_DTOR /* nothing */
|
||||
# define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS /* nothing */
|
||||
# define MOZ_IMPLICIT /* nothing */
|
||||
# define MOZ_IS_SMARTPTR_TO_REFCOUNTED /* nothing */
|
||||
# define MOZ_IS_REFPTR /* nothing */
|
||||
# define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT /* nothing */
|
||||
# define MOZ_HEAP_ALLOCATOR /* nothing */
|
||||
# define MOZ_OWNING_REF /* nothing */
|
||||
# define MOZ_NON_OWNING_REF /* nothing */
|
||||
# define MOZ_UNSAFE_REF(reason) /* nothing */
|
||||
# define MOZ_NO_ADDREF_RELEASE_ON_RETURN /* nothing */
|
||||
# define MOZ_MUST_USE_TYPE /* nothing */
|
||||
# define MOZ_NEEDS_NO_VTABLE_TYPE /* nothing */
|
||||
# define MOZ_NON_MEMMOVABLE /* nothing */
|
||||
# define MOZ_NEEDS_MEMMOVABLE_TYPE /* nothing */
|
||||
# define MOZ_NEEDS_MEMMOVABLE_MEMBERS /* nothing */
|
||||
# define MOZ_NO_DANGLING_ON_TEMPORARIES /* nothing */
|
||||
# define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS /* nothing */
|
||||
# define MOZ_INIT_OUTSIDE_CTOR /* nothing */
|
||||
# define MOZ_IS_CLASS_INIT /* nothing */
|
||||
# define MOZ_NON_PARAM /* nothing */
|
||||
# define MOZ_NON_AUTOABLE /* nothing */
|
||||
# define MOZ_REQUIRED_BASE_METHOD /* nothing */
|
||||
# define MOZ_MUST_RETURN_FROM_CALLER /* nothing */
|
||||
# define MOZ_MAY_CALL_AFTER_MUST_RETURN /* nothing */
|
||||
#endif /* MOZ_CLANG_PLUGIN */
|
||||
|
||||
#define MOZ_RAII MOZ_NON_TEMPORARY_CLASS MOZ_STACK_CLASS
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* Printf style formats. MOZ_FORMAT_PRINTF can be used to annotate a
|
||||
* function or method that is "printf-like"; this will let (some)
|
||||
* compilers check that the arguments match the template string.
|
||||
*
|
||||
* This macro takes two arguments. The first argument is the argument
|
||||
* number of the template string. The second argument is the argument
|
||||
* number of the '...' argument holding the arguments.
|
||||
*
|
||||
* Argument numbers start at 1. Note that the implicit "this"
|
||||
* argument of a non-static member function counts as an argument.
|
||||
*
|
||||
* So, for a simple case like:
|
||||
* void print_something (int whatever, const char *fmt, ...);
|
||||
* The corresponding annotation would be
|
||||
* MOZ_FORMAT_PRINTF(2, 3)
|
||||
* However, if "print_something" were a non-static member function,
|
||||
* then the annotation would be:
|
||||
* MOZ_FORMAT_PRINTF(3, 4)
|
||||
*
|
||||
* The second argument should be 0 for vprintf-like functions; that
|
||||
* is, those taking a va_list argument.
|
||||
*
|
||||
* Note that the checking is limited to standards-conforming
|
||||
* printf-likes, and in particular this should not be used for
|
||||
* PR_snprintf and friends, which are "printf-like" but which assign
|
||||
* different meanings to the various formats.
|
||||
*
|
||||
* MinGW requires special handling due to different format specifiers
|
||||
* on different platforms. The macro __MINGW_PRINTF_FORMAT maps to
|
||||
* either gnu_printf or ms_printf depending on where we are compiling
|
||||
* to avoid warnings on format specifiers that are legal.
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck) \
|
||||
__attribute__ ((format (__MINGW_PRINTF_FORMAT, stringIndex, firstToCheck)))
|
||||
#elif __GNUC__
|
||||
#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck) \
|
||||
__attribute__ ((format (printf, stringIndex, firstToCheck)))
|
||||
#else
|
||||
#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* To manually declare an XPCOM ABI-compatible virtual function, the following
|
||||
* macros can be used to handle the non-standard ABI used on Windows for COM
|
||||
* compatibility. E.g.:
|
||||
*
|
||||
* virtual ReturnType MOZ_XPCOM_ABI foo();
|
||||
*/
|
||||
#if defined(XP_WIN)
|
||||
# define MOZ_XPCOM_ABI __stdcall
|
||||
#else
|
||||
# define MOZ_XPCOM_ABI
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_Attributes_h */
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Cast operations to supplement the built-in casting operations. */
|
||||
|
||||
#ifndef mozilla_Casting_h
|
||||
#define mozilla_Casting_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Sets the outparam value of type |To| with the same underlying bit pattern of
|
||||
* |aFrom|.
|
||||
*
|
||||
* |To| and |From| must be types of the same size; be careful of cross-platform
|
||||
* size differences, or this might fail to compile on some but not all
|
||||
* platforms.
|
||||
*
|
||||
* There is also a variant that returns the value directly. In most cases, the
|
||||
* two variants should be identical. However, in the specific case of x86
|
||||
* chips, the behavior differs: returning floating-point values directly is done
|
||||
* through the x87 stack, and x87 loads and stores turn signaling NaNs into
|
||||
* quiet NaNs... silently. Returning floating-point values via outparam,
|
||||
* however, is done entirely within the SSE registers when SSE2 floating-point
|
||||
* is enabled in the compiler, which has semantics-preserving behavior you would
|
||||
* expect.
|
||||
*
|
||||
* If preserving the distinction between signaling NaNs and quiet NaNs is
|
||||
* important to you, you should use the outparam version. In all other cases,
|
||||
* you should use the direct return version.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
inline void
|
||||
BitwiseCast(const From aFrom, To* aResult)
|
||||
{
|
||||
static_assert(sizeof(From) == sizeof(To),
|
||||
"To and From must have the same size");
|
||||
union
|
||||
{
|
||||
From mFrom;
|
||||
To mTo;
|
||||
} u;
|
||||
u.mFrom = aFrom;
|
||||
*aResult = u.mTo;
|
||||
}
|
||||
|
||||
template<typename To, typename From>
|
||||
inline To
|
||||
BitwiseCast(const From aFrom)
|
||||
{
|
||||
To temp;
|
||||
BitwiseCast<To, From>(aFrom, &temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
enum ToSignedness { ToIsSigned, ToIsUnsigned };
|
||||
enum FromSignedness { FromIsSigned, FromIsUnsigned };
|
||||
|
||||
template<typename From,
|
||||
typename To,
|
||||
FromSignedness = IsSigned<From>::value ? FromIsSigned : FromIsUnsigned,
|
||||
ToSignedness = IsSigned<To>::value ? ToIsSigned : ToIsUnsigned>
|
||||
struct BoundsCheckImpl;
|
||||
|
||||
// Implicit conversions on operands to binary operations make this all a bit
|
||||
// hard to verify. Attempt to ease the pain below by *only* comparing values
|
||||
// that are obviously the same type (and will undergo no further conversions),
|
||||
// even when it's not strictly necessary, for explicitness.
|
||||
|
||||
enum UUComparison { FromIsBigger, FromIsNotBigger };
|
||||
|
||||
// Unsigned-to-unsigned range check
|
||||
|
||||
template<typename From, typename To,
|
||||
UUComparison = (sizeof(From) > sizeof(To))
|
||||
? FromIsBigger
|
||||
: FromIsNotBigger>
|
||||
struct UnsignedUnsignedCheck;
|
||||
|
||||
template<typename From, typename To>
|
||||
struct UnsignedUnsignedCheck<From, To, FromIsBigger>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return aFrom <= From(To(-1));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct UnsignedUnsignedCheck<From, To, FromIsNotBigger>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return UnsignedUnsignedCheck<From, To>::checkBounds(aFrom);
|
||||
}
|
||||
};
|
||||
|
||||
// Signed-to-unsigned range check
|
||||
|
||||
template<typename From, typename To>
|
||||
struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
if (aFrom < 0) {
|
||||
return false;
|
||||
}
|
||||
if (sizeof(To) >= sizeof(From)) {
|
||||
return true;
|
||||
}
|
||||
return aFrom <= From(To(-1));
|
||||
}
|
||||
};
|
||||
|
||||
// Unsigned-to-signed range check
|
||||
|
||||
enum USComparison { FromIsSmaller, FromIsNotSmaller };
|
||||
|
||||
template<typename From, typename To,
|
||||
USComparison = (sizeof(From) < sizeof(To))
|
||||
? FromIsSmaller
|
||||
: FromIsNotSmaller>
|
||||
struct UnsignedSignedCheck;
|
||||
|
||||
template<typename From, typename To>
|
||||
struct UnsignedSignedCheck<From, To, FromIsSmaller>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct UnsignedSignedCheck<From, To, FromIsNotSmaller>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
|
||||
return aFrom <= From(MaxValue);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return UnsignedSignedCheck<From, To>::checkBounds(aFrom);
|
||||
}
|
||||
};
|
||||
|
||||
// Signed-to-signed range check
|
||||
|
||||
template<typename From, typename To>
|
||||
struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
if (sizeof(From) <= sizeof(To)) {
|
||||
return true;
|
||||
}
|
||||
const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
|
||||
const To MinValue = -MaxValue - To(1);
|
||||
return From(MinValue) <= aFrom &&
|
||||
From(aFrom) <= From(MaxValue);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To,
|
||||
bool TypesAreIntegral = IsIntegral<From>::value &&
|
||||
IsIntegral<To>::value>
|
||||
class BoundsChecker;
|
||||
|
||||
template<typename From>
|
||||
class BoundsChecker<From, From, true>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom) { return true; }
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
class BoundsChecker<From, To, true>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return BoundsCheckImpl<From, To>::checkBounds(aFrom);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
inline bool
|
||||
IsInBounds(const From aFrom)
|
||||
{
|
||||
return BoundsChecker<From, To>::checkBounds(aFrom);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Cast a value of integral type |From| to a value of integral type |To|,
|
||||
* asserting that the cast will be a safe cast per C++ (that is, that |to| is in
|
||||
* the range of values permitted for the type |From|).
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
inline To
|
||||
AssertedCast(const From aFrom)
|
||||
{
|
||||
MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
|
||||
return static_cast<To>(aFrom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a value of integral type |From| to a value of integral type |To|,
|
||||
* release asserting that the cast will be a safe cast per C++ (that is, that
|
||||
* |to| is in the range of values permitted for the type |From|).
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
inline To
|
||||
ReleaseAssertedCast(const From aFrom)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT((detail::IsInBounds<From, To>(aFrom)));
|
||||
return static_cast<To>(aFrom);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_Casting_h */
|
||||
|
|
@ -0,0 +1,791 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Provides checked integers, detecting integer overflow and divide-by-0. */
|
||||
|
||||
#ifndef mozilla_CheckedInt_h
|
||||
#define mozilla_CheckedInt_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/IntegerTypeTraits.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<typename T> class CheckedInt;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* Step 1: manually record supported types
|
||||
*
|
||||
* What's nontrivial here is that there are different families of integer
|
||||
* types: basic integer types and stdint types. It is merrily undefined which
|
||||
* types from one family may be just typedefs for a type from another family.
|
||||
*
|
||||
* For example, on GCC 4.6, aside from the basic integer types, the only other
|
||||
* type that isn't just a typedef for some of them, is int8_t.
|
||||
*/
|
||||
|
||||
struct UnsupportedType {};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct IsSupportedPass2
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct IsSupported
|
||||
{
|
||||
static const bool value = IsSupportedPass2<IntegerType>::value;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct IsSupported<int8_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<uint8_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<int16_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<uint16_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<int32_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<uint32_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<int64_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<uint64_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<char>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<signed char>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned char>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<short>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned short>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<int>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned int>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<long>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned long>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<long long>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned long long>
|
||||
{ static const bool value = true; };
|
||||
|
||||
/*
|
||||
* Step 2: Implement the actual validity checks.
|
||||
*
|
||||
* Ideas taken from IntegerLib, code different.
|
||||
*/
|
||||
|
||||
template<typename IntegerType, size_t Size = sizeof(IntegerType)>
|
||||
struct TwiceBiggerType
|
||||
{
|
||||
typedef typename detail::StdintTypeForSizeAndSignedness<
|
||||
sizeof(IntegerType) * 2,
|
||||
IsSigned<IntegerType>::value
|
||||
>::Type Type;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct TwiceBiggerType<IntegerType, 8>
|
||||
{
|
||||
typedef UnsupportedType Type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
HasSignBit(T aX)
|
||||
{
|
||||
// In C++, right bit shifts on negative values is undefined by the standard.
|
||||
// Notice that signed-to-unsigned conversions are always well-defined in the
|
||||
// standard, as the value congruent modulo 2**n as expected. By contrast,
|
||||
// unsigned-to-signed is only well-defined if the value is representable.
|
||||
return bool(typename MakeUnsigned<T>::Type(aX) >>
|
||||
PositionOfSignBit<T>::value);
|
||||
}
|
||||
|
||||
// Bitwise ops may return a larger type, so it's good to use this inline
|
||||
// helper guaranteeing that the result is really of type T.
|
||||
template<typename T>
|
||||
inline T
|
||||
BinaryComplement(T aX)
|
||||
{
|
||||
return ~aX;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename U,
|
||||
bool IsTSigned = IsSigned<T>::value,
|
||||
bool IsUSigned = IsSigned<U>::value>
|
||||
struct DoesRangeContainRange
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T, typename U, bool Signedness>
|
||||
struct DoesRangeContainRange<T, U, Signedness, Signedness>
|
||||
{
|
||||
static const bool value = sizeof(T) >= sizeof(U);
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct DoesRangeContainRange<T, U, true, false>
|
||||
{
|
||||
static const bool value = sizeof(T) > sizeof(U);
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct DoesRangeContainRange<T, U, false, true>
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename T,
|
||||
typename U,
|
||||
bool IsTSigned = IsSigned<T>::value,
|
||||
bool IsUSigned = IsSigned<U>::value,
|
||||
bool DoesTRangeContainURange = DoesRangeContainRange<T, U>::value>
|
||||
struct IsInRangeImpl {};
|
||||
|
||||
template<typename T, typename U, bool IsTSigned, bool IsUSigned>
|
||||
struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true>
|
||||
{
|
||||
static bool constexpr run(U)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, true, true, false>
|
||||
{
|
||||
static bool constexpr run(U aX)
|
||||
{
|
||||
return aX <= MaxValue<T>::value && aX >= MinValue<T>::value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, false, false, false>
|
||||
{
|
||||
static bool constexpr run(U aX)
|
||||
{
|
||||
return aX <= MaxValue<T>::value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, true, false, false>
|
||||
{
|
||||
static bool constexpr run(U aX)
|
||||
{
|
||||
return sizeof(T) > sizeof(U) || aX <= U(MaxValue<T>::value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, false, true, false>
|
||||
{
|
||||
static bool constexpr run(U aX)
|
||||
{
|
||||
return sizeof(T) >= sizeof(U)
|
||||
? aX >= 0
|
||||
: aX >= 0 && aX <= U(MaxValue<T>::value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
inline constexpr bool
|
||||
IsInRange(U aX)
|
||||
{
|
||||
return IsInRangeImpl<T, U>::run(aX);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsAddValid(T aX, T aY)
|
||||
{
|
||||
// Addition is valid if the sign of aX+aY is equal to either that of aX or
|
||||
// that of aY. Since the value of aX+aY is undefined if we have a signed
|
||||
// type, we compute it using the unsigned type of the same size. Beware!
|
||||
// These bitwise operations can return a larger integer type, if T was a
|
||||
// small type like int8_t, so we explicitly cast to T.
|
||||
|
||||
typename MakeUnsigned<T>::Type ux = aX;
|
||||
typename MakeUnsigned<T>::Type uy = aY;
|
||||
typename MakeUnsigned<T>::Type result = ux + uy;
|
||||
return IsSigned<T>::value
|
||||
? HasSignBit(BinaryComplement(T((result ^ aX) & (result ^ aY))))
|
||||
: BinaryComplement(aX) >= aY;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsSubValid(T aX, T aY)
|
||||
{
|
||||
// Subtraction is valid if either aX and aY have same sign, or aX-aY and aX
|
||||
// have same sign. Since the value of aX-aY is undefined if we have a signed
|
||||
// type, we compute it using the unsigned type of the same size.
|
||||
typename MakeUnsigned<T>::Type ux = aX;
|
||||
typename MakeUnsigned<T>::Type uy = aY;
|
||||
typename MakeUnsigned<T>::Type result = ux - uy;
|
||||
|
||||
return IsSigned<T>::value
|
||||
? HasSignBit(BinaryComplement(T((result ^ aX) & (aX ^ aY))))
|
||||
: aX >= aY;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
bool IsTSigned = IsSigned<T>::value,
|
||||
bool TwiceBiggerTypeIsSupported =
|
||||
IsSupported<typename TwiceBiggerType<T>::Type>::value>
|
||||
struct IsMulValidImpl {};
|
||||
|
||||
template<typename T, bool IsTSigned>
|
||||
struct IsMulValidImpl<T, IsTSigned, true>
|
||||
{
|
||||
static bool run(T aX, T aY)
|
||||
{
|
||||
typedef typename TwiceBiggerType<T>::Type TwiceBiggerType;
|
||||
TwiceBiggerType product = TwiceBiggerType(aX) * TwiceBiggerType(aY);
|
||||
return IsInRange<T>(product);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsMulValidImpl<T, true, false>
|
||||
{
|
||||
static bool run(T aX, T aY)
|
||||
{
|
||||
const T max = MaxValue<T>::value;
|
||||
const T min = MinValue<T>::value;
|
||||
|
||||
if (aX == 0 || aY == 0) {
|
||||
return true;
|
||||
}
|
||||
if (aX > 0) {
|
||||
return aY > 0
|
||||
? aX <= max / aY
|
||||
: aY >= min / aX;
|
||||
}
|
||||
|
||||
// If we reach this point, we know that aX < 0.
|
||||
return aY > 0
|
||||
? aX >= min / aY
|
||||
: aY >= max / aX;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsMulValidImpl<T, false, false>
|
||||
{
|
||||
static bool run(T aX, T aY)
|
||||
{
|
||||
return aY == 0 || aX <= MaxValue<T>::value / aY;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsMulValid(T aX, T aY)
|
||||
{
|
||||
return IsMulValidImpl<T>::run(aX, aY);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsDivValid(T aX, T aY)
|
||||
{
|
||||
// Keep in mind that in the signed case, min/-1 is invalid because
|
||||
// abs(min)>max.
|
||||
return aY != 0 &&
|
||||
!(IsSigned<T>::value && aX == MinValue<T>::value && aY == T(-1));
|
||||
}
|
||||
|
||||
template<typename T, bool IsTSigned = IsSigned<T>::value>
|
||||
struct IsModValidImpl;
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsModValid(T aX, T aY)
|
||||
{
|
||||
return IsModValidImpl<T>::run(aX, aY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mod is pretty simple.
|
||||
* For now, let's just use the ANSI C definition:
|
||||
* If aX or aY are negative, the results are implementation defined.
|
||||
* Consider these invalid.
|
||||
* Undefined for aY=0.
|
||||
* The result will never exceed either aX or aY.
|
||||
*
|
||||
* Checking that aX>=0 is a warning when T is unsigned.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
struct IsModValidImpl<T, false>
|
||||
{
|
||||
static inline bool run(T aX, T aY)
|
||||
{
|
||||
return aY >= 1;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsModValidImpl<T, true>
|
||||
{
|
||||
static inline bool run(T aX, T aY)
|
||||
{
|
||||
if (aX < 0) {
|
||||
return false;
|
||||
}
|
||||
return aY >= 1;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, bool IsSigned = IsSigned<T>::value>
|
||||
struct NegateImpl;
|
||||
|
||||
template<typename T>
|
||||
struct NegateImpl<T, false>
|
||||
{
|
||||
static CheckedInt<T> negate(const CheckedInt<T>& aVal)
|
||||
{
|
||||
// Handle negation separately for signed/unsigned, for simpler code and to
|
||||
// avoid an MSVC warning negating an unsigned value.
|
||||
return CheckedInt<T>(0, aVal.isValid() && aVal.mValue == 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct NegateImpl<T, true>
|
||||
{
|
||||
static CheckedInt<T> negate(const CheckedInt<T>& aVal)
|
||||
{
|
||||
// Watch out for the min-value, which (with twos-complement) can't be
|
||||
// negated as -min-value is then (max-value + 1).
|
||||
if (!aVal.isValid() || aVal.mValue == MinValue<T>::value) {
|
||||
return CheckedInt<T>(aVal.mValue, false);
|
||||
}
|
||||
return CheckedInt<T>(-aVal.mValue, true);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/*
|
||||
* Step 3: Now define the CheckedInt class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class CheckedInt
|
||||
* @brief Integer wrapper class checking for integer overflow and other errors
|
||||
* @param T the integer type to wrap. Can be any type among the following:
|
||||
* - any basic integer type such as |int|
|
||||
* - any stdint type such as |int8_t|
|
||||
*
|
||||
* This class implements guarded integer arithmetic. Do a computation, check
|
||||
* that isValid() returns true, you then have a guarantee that no problem, such
|
||||
* as integer overflow, happened during this computation, and you can call
|
||||
* value() to get the plain integer value.
|
||||
*
|
||||
* The arithmetic operators in this class are guaranteed not to raise a signal
|
||||
* (e.g. in case of a division by zero).
|
||||
*
|
||||
* For example, suppose that you want to implement a function that computes
|
||||
* (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by
|
||||
* zero or integer overflow). You could code it as follows:
|
||||
@code
|
||||
bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult)
|
||||
{
|
||||
CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
|
||||
if (checkedResult.isValid()) {
|
||||
*aResult = checkedResult.value();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*
|
||||
* Implicit conversion from plain integers to checked integers is allowed. The
|
||||
* plain integer is checked to be in range before being casted to the
|
||||
* destination type. This means that the following lines all compile, and the
|
||||
* resulting CheckedInts are correctly detected as valid or invalid:
|
||||
* @code
|
||||
// 1 is of type int, is found to be in range for uint8_t, x is valid
|
||||
CheckedInt<uint8_t> x(1);
|
||||
// -1 is of type int, is found not to be in range for uint8_t, x is invalid
|
||||
CheckedInt<uint8_t> x(-1);
|
||||
// -1 is of type int, is found to be in range for int8_t, x is valid
|
||||
CheckedInt<int8_t> x(-1);
|
||||
// 1000 is of type int16_t, is found not to be in range for int8_t,
|
||||
// x is invalid
|
||||
CheckedInt<int8_t> x(int16_t(1000));
|
||||
// 3123456789 is of type uint32_t, is found not to be in range for int32_t,
|
||||
// x is invalid
|
||||
CheckedInt<int32_t> x(uint32_t(3123456789));
|
||||
* @endcode
|
||||
* Implicit conversion from
|
||||
* checked integers to plain integers is not allowed. As shown in the
|
||||
* above example, to get the value of a checked integer as a normal integer,
|
||||
* call value().
|
||||
*
|
||||
* Arithmetic operations between checked and plain integers is allowed; the
|
||||
* result type is the type of the checked integer.
|
||||
*
|
||||
* Checked integers of different types cannot be used in the same arithmetic
|
||||
* expression.
|
||||
*
|
||||
* There are convenience typedefs for all stdint types, of the following form
|
||||
* (these are just 2 examples):
|
||||
@code
|
||||
typedef CheckedInt<int32_t> CheckedInt32;
|
||||
typedef CheckedInt<uint16_t> CheckedUint16;
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
class CheckedInt
|
||||
{
|
||||
protected:
|
||||
T mValue;
|
||||
bool mIsValid;
|
||||
|
||||
template<typename U>
|
||||
CheckedInt(U aValue, bool aIsValid) : mValue(aValue), mIsValid(aIsValid)
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value &&
|
||||
detail::IsSupported<U>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
|
||||
friend struct detail::NegateImpl<T>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a checked integer with given @a value. The checked integer is
|
||||
* initialized as valid or invalid depending on whether the @a value
|
||||
* is in range.
|
||||
*
|
||||
* This constructor is not explicit. Instead, the type of its argument is a
|
||||
* separate template parameter, ensuring that no conversion is performed
|
||||
* before this constructor is actually called. As explained in the above
|
||||
* documentation for class CheckedInt, this constructor checks that its
|
||||
* argument is valid.
|
||||
*/
|
||||
template<typename U>
|
||||
MOZ_IMPLICIT constexpr CheckedInt(U aValue) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT
|
||||
: mValue(T(aValue)),
|
||||
mIsValid(detail::IsInRange<T>(aValue))
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value &&
|
||||
detail::IsSupported<U>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
friend class CheckedInt;
|
||||
|
||||
template<typename U>
|
||||
CheckedInt<U> toChecked() const
|
||||
{
|
||||
CheckedInt<U> ret(mValue);
|
||||
ret.mIsValid = ret.mIsValid && mIsValid;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Constructs a valid checked integer with initial value 0 */
|
||||
constexpr CheckedInt() : mValue(0), mIsValid(true)
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
|
||||
/** @returns the actual value */
|
||||
T value() const
|
||||
{
|
||||
MOZ_ASSERT(mIsValid, "Invalid checked integer (division by zero or integer overflow)");
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the checked integer is valid, i.e. is not the result
|
||||
* of an invalid operation or of an operation involving an invalid checked
|
||||
* integer
|
||||
*/
|
||||
bool isValid() const
|
||||
{
|
||||
return mIsValid;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator +(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator +=(U aRhs);
|
||||
CheckedInt& operator +=(const CheckedInt<T>& aRhs);
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator -(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator -=(U aRhs);
|
||||
CheckedInt& operator -=(const CheckedInt<T>& aRhs);
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator *(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator *=(U aRhs);
|
||||
CheckedInt& operator *=(const CheckedInt<T>& aRhs);
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator /(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator /=(U aRhs);
|
||||
CheckedInt& operator /=(const CheckedInt<T>& aRhs);
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator %(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator %=(U aRhs);
|
||||
CheckedInt& operator %=(const CheckedInt<T>& aRhs);
|
||||
|
||||
CheckedInt operator -() const
|
||||
{
|
||||
return detail::NegateImpl<T>::negate(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the left and right hand sides are valid
|
||||
* and have the same value.
|
||||
*
|
||||
* Note that these semantics are the reason why we don't offer
|
||||
* a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
|
||||
* but that would mean that whenever a or b is invalid, a!=b
|
||||
* is always true, which would be very confusing.
|
||||
*
|
||||
* For similar reasons, operators <, >, <=, >= would be very tricky to
|
||||
* specify, so we just avoid offering them.
|
||||
*
|
||||
* Notice that these == semantics are made more reasonable by these facts:
|
||||
* 1. a==b implies equality at the raw data level
|
||||
* (the converse is false, as a==b is never true among invalids)
|
||||
* 2. This is similar to the behavior of IEEE floats, where a==b
|
||||
* means that a and b have the same value *and* neither is NaN.
|
||||
*/
|
||||
bool operator ==(const CheckedInt& aOther) const
|
||||
{
|
||||
return mIsValid && aOther.mIsValid && mValue == aOther.mValue;
|
||||
}
|
||||
|
||||
/** prefix ++ */
|
||||
CheckedInt& operator++()
|
||||
{
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** postfix ++ */
|
||||
CheckedInt operator++(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/** prefix -- */
|
||||
CheckedInt& operator--()
|
||||
{
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** postfix -- */
|
||||
CheckedInt operator--(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this -= 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* The !=, <, <=, >, >= operators are disabled:
|
||||
* see the comment on operator==.
|
||||
*/
|
||||
template<typename U> bool operator !=(U aOther) const = delete;
|
||||
template<typename U> bool operator < (U aOther) const = delete;
|
||||
template<typename U> bool operator <=(U aOther) const = delete;
|
||||
template<typename U> bool operator > (U aOther) const = delete;
|
||||
template<typename U> bool operator >=(U aOther) const = delete;
|
||||
};
|
||||
|
||||
#define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
|
||||
template<typename T> \
|
||||
inline CheckedInt<T> \
|
||||
operator OP(const CheckedInt<T>& aLhs, const CheckedInt<T>& aRhs) \
|
||||
{ \
|
||||
if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \
|
||||
return CheckedInt<T>(0, false); \
|
||||
} \
|
||||
return CheckedInt<T>(aLhs.mValue OP aRhs.mValue, \
|
||||
aLhs.mIsValid && aRhs.mIsValid); \
|
||||
}
|
||||
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +)
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -)
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *)
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /)
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod, %)
|
||||
|
||||
#undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
|
||||
|
||||
// Implement castToCheckedInt<T>(x), making sure that
|
||||
// - it allows x to be either a CheckedInt<T> or any integer type
|
||||
// that can be casted to T
|
||||
// - if x is already a CheckedInt<T>, we just return a reference to it,
|
||||
// instead of copying it (optimization)
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T, typename U>
|
||||
struct CastToCheckedIntImpl
|
||||
{
|
||||
typedef CheckedInt<T> ReturnType;
|
||||
static CheckedInt<T> run(U aU) { return aU; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CastToCheckedIntImpl<T, CheckedInt<T> >
|
||||
{
|
||||
typedef const CheckedInt<T>& ReturnType;
|
||||
static const CheckedInt<T>& run(const CheckedInt<T>& aU) { return aU; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, typename U>
|
||||
inline typename detail::CastToCheckedIntImpl<T, U>::ReturnType
|
||||
castToCheckedInt(U aU)
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value &&
|
||||
detail::IsSupported<U>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
return detail::CastToCheckedIntImpl<T, U>::run(aU);
|
||||
}
|
||||
|
||||
#define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
|
||||
template<typename T> \
|
||||
template<typename U> \
|
||||
CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) \
|
||||
{ \
|
||||
*this = *this OP castToCheckedInt<T>(aRhs); \
|
||||
return *this; \
|
||||
} \
|
||||
template<typename T> \
|
||||
CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(const CheckedInt<T>& aRhs) \
|
||||
{ \
|
||||
*this = *this OP aRhs; \
|
||||
return *this; \
|
||||
} \
|
||||
template<typename T, typename U> \
|
||||
inline CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) \
|
||||
{ \
|
||||
return aLhs OP castToCheckedInt<T>(aRhs); \
|
||||
} \
|
||||
template<typename T, typename U> \
|
||||
inline CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) \
|
||||
{ \
|
||||
return castToCheckedInt<T>(aLhs) OP aRhs; \
|
||||
}
|
||||
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
|
||||
|
||||
#undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool
|
||||
operator ==(const CheckedInt<T>& aLhs, U aRhs)
|
||||
{
|
||||
return aLhs == castToCheckedInt<T>(aRhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool
|
||||
operator ==(U aLhs, const CheckedInt<T>& aRhs)
|
||||
{
|
||||
return castToCheckedInt<T>(aLhs) == aRhs;
|
||||
}
|
||||
|
||||
// Convenience typedefs.
|
||||
typedef CheckedInt<int8_t> CheckedInt8;
|
||||
typedef CheckedInt<uint8_t> CheckedUint8;
|
||||
typedef CheckedInt<int16_t> CheckedInt16;
|
||||
typedef CheckedInt<uint16_t> CheckedUint16;
|
||||
typedef CheckedInt<int32_t> CheckedInt32;
|
||||
typedef CheckedInt<uint32_t> CheckedUint32;
|
||||
typedef CheckedInt<int64_t> CheckedInt64;
|
||||
typedef CheckedInt<uint64_t> CheckedUint64;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_CheckedInt_h */
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Various compiler checks. */
|
||||
|
||||
#ifndef mozilla_Compiler_h
|
||||
#define mozilla_Compiler_h
|
||||
|
||||
#define MOZ_IS_GCC 0
|
||||
#define MOZ_IS_MSVC 0
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__)
|
||||
|
||||
# undef MOZ_IS_GCC
|
||||
# define MOZ_IS_GCC 1
|
||||
/*
|
||||
* These macros should simplify gcc version checking. For example, to check
|
||||
* for gcc 4.7.1 or later, check `#if MOZ_GCC_VERSION_AT_LEAST(4, 7, 1)`.
|
||||
*/
|
||||
# define MOZ_GCC_VERSION_AT_LEAST(major, minor, patchlevel) \
|
||||
((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) \
|
||||
>= ((major) * 10000 + (minor) * 100 + (patchlevel)))
|
||||
# define MOZ_GCC_VERSION_AT_MOST(major, minor, patchlevel) \
|
||||
((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) \
|
||||
<= ((major) * 10000 + (minor) * 100 + (patchlevel)))
|
||||
# if !MOZ_GCC_VERSION_AT_LEAST(4, 9, 0)
|
||||
# error "mfbt (and Gecko) require at least gcc 4.9 to build."
|
||||
# endif
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
# undef MOZ_IS_MSVC
|
||||
# define MOZ_IS_MSVC 1
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The situation with standard libraries is a lot worse than with compilers,
|
||||
* particularly as clang and gcc could end up using one of three or so standard
|
||||
* libraries, and they may not be up-to-snuff with newer C++11 versions. To
|
||||
* detect the library, we're going to include cstddef (which is a small header
|
||||
* which will be transitively included by everybody else at some point) to grab
|
||||
* the version macros and deduce macros from there.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
# include <cstddef>
|
||||
# ifdef _STLPORT_MAJOR
|
||||
# define MOZ_USING_STLPORT 1
|
||||
# define MOZ_STLPORT_VERSION_AT_LEAST(major, minor, patch) \
|
||||
(_STLPORT_VERSION >= ((major) << 8 | (minor) << 4 | (patch)))
|
||||
# elif defined(_LIBCPP_VERSION)
|
||||
/*
|
||||
* libc++, unfortunately, doesn't appear to have useful versioning macros.
|
||||
* Hopefully, the recommendations of N3694 with respect to standard libraries
|
||||
* will get applied instead and we won't need to worry about version numbers
|
||||
* here.
|
||||
*/
|
||||
# define MOZ_USING_LIBCXX 1
|
||||
# elif defined(__GLIBCXX__)
|
||||
# define MOZ_USING_LIBSTDCXX 1
|
||||
/*
|
||||
* libstdc++ is also annoying and doesn't give us useful versioning macros
|
||||
* for the library. If we're using gcc, then assume that libstdc++ matches
|
||||
* the compiler version. If we're using clang, we're going to have to fake
|
||||
* major/minor combinations by looking for newly-defined config macros.
|
||||
*/
|
||||
# if MOZ_IS_GCC
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) \
|
||||
MOZ_GCC_VERSION_AT_LEAST(major, minor, patch)
|
||||
# elif defined(_GLIBCXX_THROW_OR_ABORT)
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) \
|
||||
((major) < 4 || ((major) == 4 && (minor) <= 8))
|
||||
# elif defined(_GLIBCXX_NOEXCEPT)
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) \
|
||||
((major) < 4 || ((major) == 4 && (minor) <= 7))
|
||||
# elif defined(_GLIBCXX_USE_DEPRECATED)
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) \
|
||||
((major) < 4 || ((major) == 4 && (minor) <= 6))
|
||||
# elif defined(_GLIBCXX_PSEUDO_VISIBILITY)
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) \
|
||||
((major) < 4 || ((major) == 4 && (minor) <= 5))
|
||||
# elif defined(_GLIBCXX_BEGIN_EXTERN_C)
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) \
|
||||
((major) < 4 || ((major) == 4 && (minor) <= 4))
|
||||
# elif defined(_GLIBCXX_VISIBILITY_ATTR)
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) \
|
||||
((major) < 4 || ((major) == 4 && (minor) <= 3))
|
||||
# elif defined(_GLIBCXX_VISIBILITY)
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) \
|
||||
((major) < 4 || ((major) == 4 && (minor) <= 2))
|
||||
# else
|
||||
# error "Your version of libstdc++ is unknown to us and is likely too old."
|
||||
# endif
|
||||
# endif
|
||||
|
||||
// Flesh out the defines for everyone else
|
||||
# ifndef MOZ_USING_STLPORT
|
||||
# define MOZ_USING_STLPORT 0
|
||||
# define MOZ_STLPORT_VERSION_AT_LEAST(major, minor, patch) 0
|
||||
# endif
|
||||
# ifndef MOZ_USING_LIBCXX
|
||||
# define MOZ_USING_LIBCXX 0
|
||||
# endif
|
||||
# ifndef MOZ_USING_LIBSTDCXX
|
||||
# define MOZ_USING_LIBSTDCXX 0
|
||||
# define MOZ_LIBSTDCXX_VERSION_AT_LEAST(major, minor, patch) 0
|
||||
# endif
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* mozilla_Compiler_h */
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Helpers to manipulate integer types that don't fit in TypeTraits.h */
|
||||
|
||||
#ifndef mozilla_IntegerTypeTraits_h
|
||||
#define mozilla_IntegerTypeTraits_h
|
||||
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* StdintTypeForSizeAndSignedness returns the stdint integer type
|
||||
* of given size (can be 1, 2, 4 or 8) and given signedness
|
||||
* (false means unsigned, true means signed).
|
||||
*/
|
||||
template<size_t Size, bool Signedness>
|
||||
struct StdintTypeForSizeAndSignedness;
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<1, true>
|
||||
{
|
||||
typedef int8_t Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<1, false>
|
||||
{
|
||||
typedef uint8_t Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<2, true>
|
||||
{
|
||||
typedef int16_t Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<2, false>
|
||||
{
|
||||
typedef uint16_t Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<4, true>
|
||||
{
|
||||
typedef int32_t Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<4, false>
|
||||
{
|
||||
typedef uint32_t Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<8, true>
|
||||
{
|
||||
typedef int64_t Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<8, false>
|
||||
{
|
||||
typedef uint64_t Type;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<size_t Size>
|
||||
struct UnsignedStdintTypeForSize
|
||||
: detail::StdintTypeForSizeAndSignedness<Size, false>
|
||||
{};
|
||||
|
||||
template<size_t Size>
|
||||
struct SignedStdintTypeForSize
|
||||
: detail::StdintTypeForSizeAndSignedness<Size, true>
|
||||
{};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct PositionOfSignBit
|
||||
{
|
||||
static_assert(IsIntegral<IntegerType>::value,
|
||||
"PositionOfSignBit is only for integral types");
|
||||
// 8 here should be CHAR_BIT from limits.h, but the world has moved on.
|
||||
static const size_t value = 8 * sizeof(IntegerType) - 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* MinValue returns the minimum value of the given integer type as a
|
||||
* compile-time constant, which std::numeric_limits<IntegerType>::min()
|
||||
* cannot do in c++98.
|
||||
*/
|
||||
template<typename IntegerType>
|
||||
struct MinValue
|
||||
{
|
||||
private:
|
||||
static_assert(IsIntegral<IntegerType>::value,
|
||||
"MinValue is only for integral types");
|
||||
|
||||
typedef typename MakeUnsigned<IntegerType>::Type UnsignedIntegerType;
|
||||
static const size_t PosOfSignBit = PositionOfSignBit<IntegerType>::value;
|
||||
|
||||
public:
|
||||
// Bitwise ops may return a larger type, that's why we cast explicitly.
|
||||
// In C++, left bit shifts on signed values is undefined by the standard
|
||||
// unless the shifted value is representable.
|
||||
// Notice that signed-to-unsigned conversions are always well-defined in
|
||||
// the standard as the value congruent to 2**n, as expected. By contrast,
|
||||
// unsigned-to-signed is only well-defined if the value is representable.
|
||||
static const IntegerType value =
|
||||
IsSigned<IntegerType>::value
|
||||
? IntegerType(UnsignedIntegerType(1) << PosOfSignBit)
|
||||
: IntegerType(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* MaxValue returns the maximum value of the given integer type as a
|
||||
* compile-time constant, which std::numeric_limits<IntegerType>::max()
|
||||
* cannot do in c++98.
|
||||
*/
|
||||
template<typename IntegerType>
|
||||
struct MaxValue
|
||||
{
|
||||
static_assert(IsIntegral<IntegerType>::value,
|
||||
"MaxValue is only for integral types");
|
||||
|
||||
// Tricksy, but covered by the CheckedInt unit test.
|
||||
// Relies on the type of MinValue<IntegerType>::value
|
||||
// being IntegerType.
|
||||
static const IntegerType value = ~MinValue<IntegerType>::value;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_IntegerTypeTraits_h
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* MOZ_LIKELY and MOZ_UNLIKELY macros to hint to the compiler how a
|
||||
* boolean predicate should be branch-predicted.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_Likely_h
|
||||
#define mozilla_Likely_h
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
# define MOZ_LIKELY(x) (__builtin_expect(!!(x), 1))
|
||||
# define MOZ_UNLIKELY(x) (__builtin_expect(!!(x), 0))
|
||||
#else
|
||||
# define MOZ_LIKELY(x) (!!(x))
|
||||
# define MOZ_UNLIKELY(x) (!!(x))
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_Likely_h */
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* Implements various macros meant to ease the use of variadic macros.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_MacroArgs_h
|
||||
#define mozilla_MacroArgs_h
|
||||
|
||||
// Concatenates pre-processor tokens in a way that can be used with __LINE__.
|
||||
#define MOZ_CONCAT2(x, y) x ## y
|
||||
#define MOZ_CONCAT(x, y) MOZ_CONCAT2(x, y)
|
||||
|
||||
/*
|
||||
* MOZ_ARG_COUNT(...) counts the number of variadic arguments.
|
||||
* You must pass in between 0 and 50 (inclusive) variadic arguments.
|
||||
* For example:
|
||||
*
|
||||
* MOZ_ARG_COUNT() expands to 0
|
||||
* MOZ_ARG_COUNT(a) expands to 1
|
||||
* MOZ_ARG_COUNT(a, b) expands to 2
|
||||
*
|
||||
* Implementation notes:
|
||||
* The `##__VA_ARGS__` form is a GCC extension that removes the comma if
|
||||
* __VA_ARGS__ is empty. It is supported by Clang too. MSVC ignores ##,
|
||||
* and its default behavior is already to strip the comma when __VA_ARGS__
|
||||
* is empty.
|
||||
*
|
||||
* So MOZ_MACROARGS_ARG_COUNT_HELPER() expands to
|
||||
* (_, 50, 49, ...)
|
||||
* MOZ_MACROARGS_ARG_COUNT_HELPER(a) expands to
|
||||
* (_, a, 50, 49, ...)
|
||||
* etc.
|
||||
*/
|
||||
#define MOZ_ARG_COUNT(...) \
|
||||
MOZ_MACROARGS_ARG_COUNT_HELPER2(MOZ_MACROARGS_ARG_COUNT_HELPER(__VA_ARGS__))
|
||||
|
||||
#define MOZ_MACROARGS_ARG_COUNT_HELPER(...) (_, ##__VA_ARGS__, \
|
||||
50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \
|
||||
40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \
|
||||
30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
|
||||
20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \
|
||||
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
|
||||
#define MOZ_MACROARGS_ARG_COUNT_HELPER2(aArgs) \
|
||||
MOZ_MACROARGS_ARG_COUNT_HELPER3 aArgs
|
||||
|
||||
#define MOZ_MACROARGS_ARG_COUNT_HELPER3(a0, \
|
||||
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
|
||||
a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, \
|
||||
a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, \
|
||||
a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, \
|
||||
a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, \
|
||||
a51, ...) a51
|
||||
|
||||
/*
|
||||
* MOZ_PASTE_PREFIX_AND_ARG_COUNT(aPrefix, ...) counts the number of variadic
|
||||
* arguments and prefixes it with |aPrefix|. For example:
|
||||
*
|
||||
* MOZ_PASTE_PREFIX_AND_ARG_COUNT(, foo, 42) expands to 2
|
||||
* MOZ_PASTE_PREFIX_AND_ARG_COUNT(A, foo, 42, bar) expands to A3
|
||||
* MOZ_PASTE_PREFIX_AND_ARG_COUNT(A) expands to A0
|
||||
* MOZ_PASTE_PREFIX_AND_ARG_COUNT() expands to 0, but MSVC warns there
|
||||
* aren't enough arguments given.
|
||||
*
|
||||
* You must pass in between 0 and 50 (inclusive) variadic arguments, past
|
||||
* |aPrefix|.
|
||||
*/
|
||||
#define MOZ_PASTE_PREFIX_AND_ARG_COUNT_GLUE(a, b) a b
|
||||
#define MOZ_PASTE_PREFIX_AND_ARG_COUNT(aPrefix, ...) \
|
||||
MOZ_PASTE_PREFIX_AND_ARG_COUNT_GLUE( \
|
||||
MOZ_CONCAT, (aPrefix, MOZ_ARG_COUNT(__VA_ARGS__)))
|
||||
|
||||
/*
|
||||
* MOZ_ARGS_AFTER_N expands to its arguments excluding the first |N|
|
||||
* arguments. For example:
|
||||
*
|
||||
* MOZ_ARGS_AFTER_2(a, b, c, d) expands to: c, d
|
||||
*/
|
||||
#define MOZ_ARGS_AFTER_1(a1, ...) __VA_ARGS__
|
||||
#define MOZ_ARGS_AFTER_2(a1, a2, ...) __VA_ARGS__
|
||||
|
||||
/*
|
||||
* MOZ_ARG_N expands to its |N|th argument.
|
||||
*/
|
||||
#define MOZ_ARG_1(a1, ...) a1
|
||||
#define MOZ_ARG_2(a1, a2, ...) a2
|
||||
|
||||
#endif /* mozilla_MacroArgs_h */
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* C++11-style, but C++98-usable, "move references" implementation. */
|
||||
|
||||
#ifndef mozilla_Move_h
|
||||
#define mozilla_Move_h
|
||||
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* "Move" References
|
||||
*
|
||||
* Some types can be copied much more efficiently if we know the original's
|
||||
* value need not be preserved --- that is, if we are doing a "move", not a
|
||||
* "copy". For example, if we have:
|
||||
*
|
||||
* Vector<T> u;
|
||||
* Vector<T> v(u);
|
||||
*
|
||||
* the constructor for v must apply a copy constructor to each element of u ---
|
||||
* taking time linear in the length of u. However, if we know we will not need u
|
||||
* any more once v has been initialized, then we could initialize v very
|
||||
* efficiently simply by stealing u's dynamically allocated buffer and giving it
|
||||
* to v --- a constant-time operation, regardless of the size of u.
|
||||
*
|
||||
* Moves often appear in container implementations. For example, when we append
|
||||
* to a vector, we may need to resize its buffer. This entails moving each of
|
||||
* its extant elements from the old, smaller buffer to the new, larger buffer.
|
||||
* But once the elements have been migrated, we're just going to throw away the
|
||||
* old buffer; we don't care if they still have their values. So if the vector's
|
||||
* element type can implement "move" more efficiently than "copy", the vector
|
||||
* resizing should by all means use a "move" operation. Hash tables should also
|
||||
* use moves when resizing their internal array as entries are added and
|
||||
* removed.
|
||||
*
|
||||
* The details of the optimization, and whether it's worth applying, vary
|
||||
* from one type to the next: copying an 'int' is as cheap as moving it, so
|
||||
* there's no benefit in distinguishing 'int' moves from copies. And while
|
||||
* some constructor calls for complex types are moves, many really have to
|
||||
* be copies, and can't be optimized this way. So we need:
|
||||
*
|
||||
* 1) a way for a type (like Vector) to announce that it can be moved more
|
||||
* efficiently than it can be copied, and provide an implementation of that
|
||||
* move operation; and
|
||||
*
|
||||
* 2) a way for a particular invocation of a copy constructor to say that it's
|
||||
* really a move, not a copy, and that the value of the original isn't
|
||||
* important afterwards (although it must still be safe to destroy).
|
||||
*
|
||||
* If a constructor has a single argument of type 'T&&' (an 'rvalue reference
|
||||
* to T'), that indicates that it is a 'move constructor'. That's 1). It should
|
||||
* move, not copy, its argument into the object being constructed. It may leave
|
||||
* the original in any safely-destructible state.
|
||||
*
|
||||
* If a constructor's argument is an rvalue, as in 'C(f(x))' or 'C(x + y)', as
|
||||
* opposed to an lvalue, as in 'C(x)', then overload resolution will prefer the
|
||||
* move constructor, if there is one. The 'mozilla::Move' function, defined in
|
||||
* this file, is an identity function you can use in a constructor invocation to
|
||||
* make any argument into an rvalue, like this: C(Move(x)). That's 2). (You
|
||||
* could use any function that works, but 'Move' indicates your intention
|
||||
* clearly.)
|
||||
*
|
||||
* Where we might define a copy constructor for a class C like this:
|
||||
*
|
||||
* C(const C& rhs) { ... copy rhs to this ... }
|
||||
*
|
||||
* we would declare a move constructor like this:
|
||||
*
|
||||
* C(C&& rhs) { .. move rhs to this ... }
|
||||
*
|
||||
* And where we might perform a copy like this:
|
||||
*
|
||||
* C c2(c1);
|
||||
*
|
||||
* we would perform a move like this:
|
||||
*
|
||||
* C c2(Move(c1));
|
||||
*
|
||||
* Note that 'T&&' implicitly converts to 'T&'. So you can pass a 'T&&' to an
|
||||
* ordinary copy constructor for a type that doesn't support a special move
|
||||
* constructor, and you'll just get a copy. This means that templates can use
|
||||
* Move whenever they know they won't use the original value any more, even if
|
||||
* they're not sure whether the type at hand has a specialized move constructor.
|
||||
* If it doesn't, the 'T&&' will just convert to a 'T&', and the ordinary copy
|
||||
* constructor will apply.
|
||||
*
|
||||
* A class with a move constructor can also provide a move assignment operator.
|
||||
* A generic definition would run this's destructor, and then apply the move
|
||||
* constructor to *this's memory. A typical definition:
|
||||
*
|
||||
* C& operator=(C&& rhs) {
|
||||
* MOZ_ASSERT(&rhs != this, "self-moves are prohibited");
|
||||
* this->~C();
|
||||
* new(this) C(Move(rhs));
|
||||
* return *this;
|
||||
* }
|
||||
*
|
||||
* With that in place, one can write move assignments like this:
|
||||
*
|
||||
* c2 = Move(c1);
|
||||
*
|
||||
* This destroys c2, moves c1's value to c2, and leaves c1 in an undefined but
|
||||
* destructible state.
|
||||
*
|
||||
* As we say, a move must leave the original in a "destructible" state. The
|
||||
* original's destructor will still be called, so if a move doesn't
|
||||
* actually steal all its resources, that's fine. We require only that the
|
||||
* move destination must take on the original's value; and that destructing
|
||||
* the original must not break the move destination.
|
||||
*
|
||||
* (Opinions differ on whether move assignment operators should deal with move
|
||||
* assignment of an object onto itself. It seems wise to either handle that
|
||||
* case, or assert that it does not occur.)
|
||||
*
|
||||
* Forwarding:
|
||||
*
|
||||
* Sometimes we want copy construction or assignment if we're passed an ordinary
|
||||
* value, but move construction if passed an rvalue reference. For example, if
|
||||
* our constructor takes two arguments and either could usefully be a move, it
|
||||
* seems silly to write out all four combinations:
|
||||
*
|
||||
* C::C(X& x, Y& y) : x(x), y(y) { }
|
||||
* C::C(X& x, Y&& y) : x(x), y(Move(y)) { }
|
||||
* C::C(X&& x, Y& y) : x(Move(x)), y(y) { }
|
||||
* C::C(X&& x, Y&& y) : x(Move(x)), y(Move(y)) { }
|
||||
*
|
||||
* To avoid this, C++11 has tweaks to make it possible to write what you mean.
|
||||
* The four constructor overloads above can be written as one constructor
|
||||
* template like so[0]:
|
||||
*
|
||||
* template <typename XArg, typename YArg>
|
||||
* C::C(XArg&& x, YArg&& y) : x(Forward<XArg>(x)), y(Forward<YArg>(y)) { }
|
||||
*
|
||||
* ("'Don't Repeat Yourself'? What's that?")
|
||||
*
|
||||
* This takes advantage of two new rules in C++11:
|
||||
*
|
||||
* - First, when a function template takes an argument that is an rvalue
|
||||
* reference to a template argument (like 'XArg&& x' and 'YArg&& y' above),
|
||||
* then when the argument is applied to an lvalue, the template argument
|
||||
* resolves to 'T&'; and when it is applied to an rvalue, the template
|
||||
* argument resolves to 'T'. Thus, in a call to C::C like:
|
||||
*
|
||||
* X foo(int);
|
||||
* Y yy;
|
||||
*
|
||||
* C(foo(5), yy)
|
||||
*
|
||||
* XArg would resolve to 'X', and YArg would resolve to 'Y&'.
|
||||
*
|
||||
* - Second, Whereas C++ used to forbid references to references, C++11 defines
|
||||
* 'collapsing rules': 'T& &', 'T&& &', and 'T& &&' (that is, any combination
|
||||
* involving an lvalue reference) now collapse to simply 'T&'; and 'T&& &&'
|
||||
* collapses to 'T&&'.
|
||||
*
|
||||
* Thus, in the call above, 'XArg&&' is 'X&&'; and 'YArg&&' is 'Y& &&', which
|
||||
* collapses to 'Y&'. Because the arguments are declared as rvalue references
|
||||
* to template arguments, the lvalue-ness "shines through" where present.
|
||||
*
|
||||
* Then, the 'Forward<T>' function --- you must invoke 'Forward' with its type
|
||||
* argument --- returns an lvalue reference or an rvalue reference to its
|
||||
* argument, depending on what T is. In our unified constructor definition, that
|
||||
* means that we'll invoke either the copy or move constructors for x and y,
|
||||
* depending on what we gave C's constructor. In our call, we'll move 'foo()'
|
||||
* into 'x', but copy 'yy' into 'y'.
|
||||
*
|
||||
* This header file defines Move and Forward in the mozilla namespace. It's up
|
||||
* to individual containers to annotate moves as such, by calling Move; and it's
|
||||
* up to individual types to define move constructors and assignment operators
|
||||
* when valuable.
|
||||
*
|
||||
* (C++11 says that the <utility> header file should define 'std::move' and
|
||||
* 'std::forward', which are just like our 'Move' and 'Forward'; but those
|
||||
* definitions aren't available in that header on all our platforms, so we
|
||||
* define them ourselves here.)
|
||||
*
|
||||
* 0. This pattern is known as "perfect forwarding". Interestingly, it is not
|
||||
* actually perfect, and it can't forward all possible argument expressions!
|
||||
* There is a C++11 issue: you can't form a reference to a bit-field. As a
|
||||
* workaround, assign the bit-field to a local variable and use that:
|
||||
*
|
||||
* // C is as above
|
||||
* struct S { int x : 1; } s;
|
||||
* C(s.x, 0); // BAD: s.x is a reference to a bit-field, can't form those
|
||||
* int tmp = s.x;
|
||||
* C(tmp, 0); // OK: tmp not a bit-field
|
||||
*/
|
||||
|
||||
/**
|
||||
* Identical to std::Move(); this is necessary until our stlport supports
|
||||
* std::move().
|
||||
*/
|
||||
template<typename T>
|
||||
inline typename RemoveReference<T>::Type&&
|
||||
Move(T&& aX)
|
||||
{
|
||||
return static_cast<typename RemoveReference<T>::Type&&>(aX);
|
||||
}
|
||||
|
||||
/**
|
||||
* These two overloads are identical to std::forward(); they are necessary until
|
||||
* our stlport supports std::forward().
|
||||
*/
|
||||
template<typename T>
|
||||
inline T&&
|
||||
Forward(typename RemoveReference<T>::Type& aX)
|
||||
{
|
||||
return static_cast<T&&>(aX);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T&&
|
||||
Forward(typename RemoveReference<T>::Type&& aX)
|
||||
{
|
||||
static_assert(!IsLvalueReference<T>::value,
|
||||
"misuse of Forward detected! try the other overload");
|
||||
return static_cast<T&&>(aX);
|
||||
}
|
||||
|
||||
/** Swap |aX| and |aY| using move-construction if possible. */
|
||||
template<typename T>
|
||||
inline void
|
||||
Swap(T& aX, T& aY)
|
||||
{
|
||||
T tmp(Move(aX));
|
||||
aX = Move(aY);
|
||||
aY = Move(tmp);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_Move_h */
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* A class holding a pair of objects that tries to conserve storage space. */
|
||||
|
||||
#ifndef mozilla_Pair_h
|
||||
#define mozilla_Pair_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
enum StorageType { AsBase, AsMember };
|
||||
|
||||
// Optimize storage using the Empty Base Optimization -- that empty base classes
|
||||
// don't take up space -- to optimize size when one or the other class is
|
||||
// stateless and can be used as a base class.
|
||||
//
|
||||
// The extra conditions on storage for B are necessary so that PairHelper won't
|
||||
// ambiguously inherit from either A or B, such that one or the other base class
|
||||
// would be inaccessible.
|
||||
template<typename A, typename B,
|
||||
detail::StorageType =
|
||||
IsEmpty<A>::value ? detail::AsBase : detail::AsMember,
|
||||
detail::StorageType =
|
||||
IsEmpty<B>::value && !IsBaseOf<A, B>::value && !IsBaseOf<B, A>::value
|
||||
? detail::AsBase
|
||||
: detail::AsMember>
|
||||
struct PairHelper;
|
||||
|
||||
template<typename A, typename B>
|
||||
struct PairHelper<A, B, AsMember, AsMember>
|
||||
{
|
||||
protected:
|
||||
template<typename AArg, typename BArg>
|
||||
PairHelper(AArg&& aA, BArg&& aB)
|
||||
: mFirstA(Forward<AArg>(aA)),
|
||||
mSecondB(Forward<BArg>(aB))
|
||||
{}
|
||||
|
||||
A& first() { return mFirstA; }
|
||||
const A& first() const { return mFirstA; }
|
||||
B& second() { return mSecondB; }
|
||||
const B& second() const { return mSecondB; }
|
||||
|
||||
void swap(PairHelper& aOther)
|
||||
{
|
||||
Swap(mFirstA, aOther.mFirstA);
|
||||
Swap(mSecondB, aOther.mSecondB);
|
||||
}
|
||||
|
||||
private:
|
||||
A mFirstA;
|
||||
B mSecondB;
|
||||
};
|
||||
|
||||
template<typename A, typename B>
|
||||
struct PairHelper<A, B, AsMember, AsBase> : private B
|
||||
{
|
||||
protected:
|
||||
template<typename AArg, typename BArg>
|
||||
PairHelper(AArg&& aA, BArg&& aB)
|
||||
: B(Forward<BArg>(aB)),
|
||||
mFirstA(Forward<AArg>(aA))
|
||||
{}
|
||||
|
||||
A& first() { return mFirstA; }
|
||||
const A& first() const { return mFirstA; }
|
||||
B& second() { return *this; }
|
||||
const B& second() const { return *this; }
|
||||
|
||||
void swap(PairHelper& aOther)
|
||||
{
|
||||
Swap(mFirstA, aOther.mFirstA);
|
||||
Swap(static_cast<B&>(*this), static_cast<B&>(aOther));
|
||||
}
|
||||
|
||||
private:
|
||||
A mFirstA;
|
||||
};
|
||||
|
||||
template<typename A, typename B>
|
||||
struct PairHelper<A, B, AsBase, AsMember> : private A
|
||||
{
|
||||
protected:
|
||||
template<typename AArg, typename BArg>
|
||||
PairHelper(AArg&& aA, BArg&& aB)
|
||||
: A(Forward<AArg>(aA)),
|
||||
mSecondB(Forward<BArg>(aB))
|
||||
{}
|
||||
|
||||
A& first() { return *this; }
|
||||
const A& first() const { return *this; }
|
||||
B& second() { return mSecondB; }
|
||||
const B& second() const { return mSecondB; }
|
||||
|
||||
void swap(PairHelper& aOther)
|
||||
{
|
||||
Swap(static_cast<A&>(*this), static_cast<A&>(aOther));
|
||||
Swap(mSecondB, aOther.mSecondB);
|
||||
}
|
||||
|
||||
private:
|
||||
B mSecondB;
|
||||
};
|
||||
|
||||
template<typename A, typename B>
|
||||
struct PairHelper<A, B, AsBase, AsBase> : private A, private B
|
||||
{
|
||||
protected:
|
||||
template<typename AArg, typename BArg>
|
||||
PairHelper(AArg&& aA, BArg&& aB)
|
||||
: A(Forward<AArg>(aA)),
|
||||
B(Forward<BArg>(aB))
|
||||
{}
|
||||
|
||||
A& first() { return static_cast<A&>(*this); }
|
||||
const A& first() const { return static_cast<A&>(*this); }
|
||||
B& second() { return static_cast<B&>(*this); }
|
||||
const B& second() const { return static_cast<B&>(*this); }
|
||||
|
||||
void swap(PairHelper& aOther)
|
||||
{
|
||||
Swap(static_cast<A&>(*this), static_cast<A&>(aOther));
|
||||
Swap(static_cast<B&>(*this), static_cast<B&>(aOther));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Pair is the logical concatenation of an instance of A with an instance B.
|
||||
* Space is conserved when possible. Neither A nor B may be a final class.
|
||||
*
|
||||
* It's typically clearer to have individual A and B member fields. Except if
|
||||
* you want the space-conserving qualities of Pair, you're probably better off
|
||||
* not using this!
|
||||
*
|
||||
* No guarantees are provided about the memory layout of A and B, the order of
|
||||
* initialization or destruction of A and B, and so on. (This is approximately
|
||||
* required to optimize space usage.) The first/second names are merely
|
||||
* conceptual!
|
||||
*/
|
||||
template<typename A, typename B>
|
||||
struct Pair
|
||||
: private detail::PairHelper<A, B>
|
||||
{
|
||||
typedef typename detail::PairHelper<A, B> Base;
|
||||
|
||||
public:
|
||||
template<typename AArg, typename BArg>
|
||||
Pair(AArg&& aA, BArg&& aB)
|
||||
: Base(Forward<AArg>(aA), Forward<BArg>(aB))
|
||||
{}
|
||||
|
||||
Pair(Pair&& aOther)
|
||||
: Base(Move(aOther.first()), Move(aOther.second()))
|
||||
{ }
|
||||
|
||||
Pair(const Pair& aOther) = default;
|
||||
|
||||
Pair& operator=(Pair&& aOther)
|
||||
{
|
||||
MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
|
||||
|
||||
first() = Move(aOther.first());
|
||||
second() = Move(aOther.second());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Pair& operator=(const Pair& aOther) = default;
|
||||
|
||||
/** The A instance. */
|
||||
using Base::first;
|
||||
/** The B instance. */
|
||||
using Base::second;
|
||||
|
||||
/** Swap this pair with another pair. */
|
||||
void swap(Pair& aOther) { Base::swap(aOther); }
|
||||
};
|
||||
|
||||
template<typename A, class B>
|
||||
void
|
||||
Swap(Pair<A, B>& aX, Pair<A, B>& aY)
|
||||
{
|
||||
aX.swap(aY);
|
||||
}
|
||||
|
||||
/**
|
||||
* MakePair allows you to construct a Pair instance using type inference. A call
|
||||
* like this:
|
||||
*
|
||||
* MakePair(Foo(), Bar())
|
||||
*
|
||||
* will return a Pair<Foo, Bar>.
|
||||
*/
|
||||
template<typename A, typename B>
|
||||
Pair<typename RemoveCV<typename RemoveReference<A>::Type>::Type,
|
||||
typename RemoveCV<typename RemoveReference<B>::Type>::Type>
|
||||
MakePair(A&& aA, B&& aB)
|
||||
{
|
||||
return
|
||||
Pair<typename RemoveCV<typename RemoveReference<A>::Type>::Type,
|
||||
typename RemoveCV<typename RemoveReference<B>::Type>::Type>(
|
||||
Forward<A>(aA),
|
||||
Forward<B>(aB));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_Pair_h */
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_StaticAnalysisFunctions_h
|
||||
#define mozilla_StaticAnalysisFunctions_h
|
||||
|
||||
#ifndef __cplusplus
|
||||
#ifndef bool
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
#endif
|
||||
/*
|
||||
* Functions that are used as markers in Gecko code for static analysis. Their
|
||||
* purpose is to have different AST nodes generated during compile time and to
|
||||
* match them based on different checkers implemented in build/clang-plugin
|
||||
*/
|
||||
|
||||
#ifdef MOZ_CLANG_PLUGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
/**
|
||||
* MOZ_KnownLive - used to opt an argument out of the CanRunScript checker so
|
||||
* that we don't check it if is a strong ref.
|
||||
*
|
||||
* Example:
|
||||
* canRunScript(MOZ_KnownLive(rawPointer));
|
||||
*/
|
||||
template <typename T>
|
||||
static MOZ_ALWAYS_INLINE T* MOZ_KnownLive(T* ptr) { return ptr; }
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* MOZ_AssertAssignmentTest - used in MOZ_ASSERT in order to test the possible
|
||||
* presence of assignment instead of logical comparisons.
|
||||
*
|
||||
* Example:
|
||||
* MOZ_ASSERT(retVal = true);
|
||||
*/
|
||||
static MOZ_ALWAYS_INLINE bool MOZ_AssertAssignmentTest(bool exprResult) {
|
||||
return exprResult;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define MOZ_CHECK_ASSERT_ASSIGNMENT(expr) MOZ_AssertAssignmentTest(!!(expr))
|
||||
|
||||
#else
|
||||
|
||||
#define MOZ_CHECK_ASSERT_ASSIGNMENT(expr) (!!(expr))
|
||||
|
||||
#endif /* MOZ_CLANG_PLUGIN */
|
||||
#endif /* StaticAnalysisFunctions_h */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,139 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* mfbt foundational types and macros. */
|
||||
|
||||
#ifndef mozilla_Types_h
|
||||
#define mozilla_Types_h
|
||||
|
||||
/*
|
||||
* This header must be valid C and C++, includable by code embedding either
|
||||
* SpiderMonkey or Gecko.
|
||||
*/
|
||||
|
||||
/* Expose all <stdint.h> types and size_t. */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Implement compiler and linker macros needed for APIs. */
|
||||
|
||||
/*
|
||||
* MOZ_EXPORT is used to declare and define a symbol or type which is externally
|
||||
* visible to users of the current library. It encapsulates various decorations
|
||||
* needed to properly export the method's symbol.
|
||||
*
|
||||
* api.h:
|
||||
* extern MOZ_EXPORT int MeaningOfLife(void);
|
||||
* extern MOZ_EXPORT int LuggageCombination;
|
||||
*
|
||||
* api.c:
|
||||
* int MeaningOfLife(void) { return 42; }
|
||||
* int LuggageCombination = 12345;
|
||||
*
|
||||
* If you are merely sharing a method across files, just use plain |extern|.
|
||||
* These macros are designed for use by library interfaces -- not for normal
|
||||
* methods or data used cross-file.
|
||||
*/
|
||||
#if defined(WIN32)
|
||||
# define MOZ_EXPORT __declspec(dllexport)
|
||||
#else /* Unix */
|
||||
# ifdef HAVE_VISIBILITY_ATTRIBUTE
|
||||
# define MOZ_EXPORT __attribute__((visibility("default")))
|
||||
# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
|
||||
# define MOZ_EXPORT __global
|
||||
# else
|
||||
# define MOZ_EXPORT /* nothing */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Whereas implementers use MOZ_EXPORT to declare and define library symbols,
|
||||
* users use MOZ_IMPORT_API and MOZ_IMPORT_DATA to access them. Most often the
|
||||
* implementer of the library will expose an API macro which expands to either
|
||||
* the export or import version of the macro, depending upon the compilation
|
||||
* mode.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
# if defined(__MWERKS__)
|
||||
# define MOZ_IMPORT_API /* nothing */
|
||||
# else
|
||||
# define MOZ_IMPORT_API __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define MOZ_IMPORT_API MOZ_EXPORT
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(__MWERKS__)
|
||||
# define MOZ_IMPORT_DATA __declspec(dllimport)
|
||||
#else
|
||||
# define MOZ_IMPORT_DATA MOZ_EXPORT
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Consistent with the above comment, the MFBT_API and MFBT_DATA macros expose
|
||||
* export mfbt declarations when building mfbt, and they expose import mfbt
|
||||
* declarations when using mfbt.
|
||||
*/
|
||||
#if defined(IMPL_MFBT) || (defined(JS_STANDALONE) && !defined(MOZ_MEMORY) && (defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API)))
|
||||
# define MFBT_API MOZ_EXPORT
|
||||
# define MFBT_DATA MOZ_EXPORT
|
||||
#else
|
||||
# if defined(JS_STANDALONE) && !defined(MOZ_MEMORY) && defined(STATIC_JS_API)
|
||||
# define MFBT_API
|
||||
# define MFBT_DATA
|
||||
# else
|
||||
/*
|
||||
* On linux mozglue is linked in the program and we link libxul.so with
|
||||
* -z,defs. Normally that causes the linker to reject undefined references in
|
||||
* libxul.so, but as a loophole it allows undefined references to weak
|
||||
* symbols. We add the weak attribute to the import version of the MFBT API
|
||||
* macros to exploit this.
|
||||
*/
|
||||
# if defined(MOZ_GLUE_IN_PROGRAM)
|
||||
# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API
|
||||
# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA
|
||||
# else
|
||||
# define MFBT_API MOZ_IMPORT_API
|
||||
# define MFBT_DATA MOZ_IMPORT_DATA
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* C symbols in C++ code must be declared immediately within |extern "C"|
|
||||
* blocks. However, in C code, they need not be declared specially. This
|
||||
* difference is abstracted behind the MOZ_BEGIN_EXTERN_C and MOZ_END_EXTERN_C
|
||||
* macros, so that the user need not know whether he is being used in C or C++
|
||||
* code.
|
||||
*
|
||||
* MOZ_BEGIN_EXTERN_C
|
||||
*
|
||||
* extern MOZ_EXPORT int MostRandomNumber(void);
|
||||
* ...other declarations...
|
||||
*
|
||||
* MOZ_END_EXTERN_C
|
||||
*
|
||||
* This said, it is preferable to just use |extern "C"| in C++ header files for
|
||||
* its greater clarity.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
# define MOZ_BEGIN_EXTERN_C extern "C" {
|
||||
# define MOZ_END_EXTERN_C }
|
||||
#else
|
||||
# define MOZ_BEGIN_EXTERN_C
|
||||
# define MOZ_END_EXTERN_C
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GCC's typeof is available when decltype is not.
|
||||
*/
|
||||
#if defined(__GNUC__) && defined(__cplusplus) && \
|
||||
!defined(__GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L
|
||||
# define decltype __typeof__
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_Types_h */
|
||||
|
|
@ -0,0 +1,697 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Smart pointer managing sole ownership of a resource. */
|
||||
|
||||
#ifndef mozilla_UniquePtr_h
|
||||
#define mozilla_UniquePtr_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Pair.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<typename T> class DefaultDelete;
|
||||
template<typename T, class D = DefaultDelete<T>> class UniquePtr;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct HasPointerTypeHelper
|
||||
{
|
||||
template <class U> static double Test(...);
|
||||
template <class U> static char Test(typename U::pointer* = 0);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class HasPointerType : public IntegralConstant<bool, sizeof(HasPointerTypeHelper::Test<T>(0)) == 1>
|
||||
{
|
||||
};
|
||||
|
||||
template <class T, class D, bool = HasPointerType<D>::value>
|
||||
struct PointerTypeImpl
|
||||
{
|
||||
typedef typename D::pointer Type;
|
||||
};
|
||||
|
||||
template <class T, class D>
|
||||
struct PointerTypeImpl<T, D, false>
|
||||
{
|
||||
typedef T* Type;
|
||||
};
|
||||
|
||||
template <class T, class D>
|
||||
struct PointerType
|
||||
{
|
||||
typedef typename PointerTypeImpl<T, typename RemoveReference<D>::Type>::Type Type;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* UniquePtr is a smart pointer that wholly owns a resource. Ownership may be
|
||||
* transferred out of a UniquePtr through explicit action, but otherwise the
|
||||
* resource is destroyed when the UniquePtr is destroyed.
|
||||
*
|
||||
* UniquePtr is similar to C++98's std::auto_ptr, but it improves upon auto_ptr
|
||||
* in one crucial way: it's impossible to copy a UniquePtr. Copying an auto_ptr
|
||||
* obviously *can't* copy ownership of its singly-owned resource. So what
|
||||
* happens if you try to copy one? Bizarrely, ownership is implicitly
|
||||
* *transferred*, preserving single ownership but breaking code that assumes a
|
||||
* copy of an object is identical to the original. (This is why auto_ptr is
|
||||
* prohibited in STL containers.)
|
||||
*
|
||||
* UniquePtr solves this problem by being *movable* rather than copyable.
|
||||
* Instead of passing a |UniquePtr u| directly to the constructor or assignment
|
||||
* operator, you pass |Move(u)|. In doing so you indicate that you're *moving*
|
||||
* ownership out of |u|, into the target of the construction/assignment. After
|
||||
* the transfer completes, |u| contains |nullptr| and may be safely destroyed.
|
||||
* This preserves single ownership but also allows UniquePtr to be moved by
|
||||
* algorithms that have been made move-safe. (Note: if |u| is instead a
|
||||
* temporary expression, don't use |Move()|: just pass the expression, because
|
||||
* it's already move-ready. For more information see Move.h.)
|
||||
*
|
||||
* UniquePtr is also better than std::auto_ptr in that the deletion operation is
|
||||
* customizable. An optional second template parameter specifies a class that
|
||||
* (through its operator()(T*)) implements the desired deletion policy. If no
|
||||
* policy is specified, mozilla::DefaultDelete<T> is used -- which will either
|
||||
* |delete| or |delete[]| the resource, depending whether the resource is an
|
||||
* array. Custom deletion policies ideally should be empty classes (no member
|
||||
* fields, no member fields in base classes, no virtual methods/inheritance),
|
||||
* because then UniquePtr can be just as efficient as a raw pointer.
|
||||
*
|
||||
* Use of UniquePtr proceeds like so:
|
||||
*
|
||||
* UniquePtr<int> g1; // initializes to nullptr
|
||||
* g1.reset(new int); // switch resources using reset()
|
||||
* g1 = nullptr; // clears g1, deletes the int
|
||||
*
|
||||
* UniquePtr<int> g2(new int); // owns that int
|
||||
* int* p = g2.release(); // g2 leaks its int -- still requires deletion
|
||||
* delete p; // now freed
|
||||
*
|
||||
* struct S { int x; S(int x) : x(x) {} };
|
||||
* UniquePtr<S> g3, g4(new S(5));
|
||||
* g3 = Move(g4); // g3 owns the S, g4 cleared
|
||||
* S* p = g3.get(); // g3 still owns |p|
|
||||
* assert(g3->x == 5); // operator-> works (if .get() != nullptr)
|
||||
* assert((*g3).x == 5); // also operator* (again, if not cleared)
|
||||
* Swap(g3, g4); // g4 now owns the S, g3 cleared
|
||||
* g3.swap(g4); // g3 now owns the S, g4 cleared
|
||||
* UniquePtr<S> g5(Move(g3)); // g5 owns the S, g3 cleared
|
||||
* g5.reset(); // deletes the S, g5 cleared
|
||||
*
|
||||
* struct FreePolicy { void operator()(void* p) { free(p); } };
|
||||
* UniquePtr<int, FreePolicy> g6(static_cast<int*>(malloc(sizeof(int))));
|
||||
* int* ptr = g6.get();
|
||||
* g6 = nullptr; // calls free(ptr)
|
||||
*
|
||||
* Now, carefully note a few things you *can't* do:
|
||||
*
|
||||
* UniquePtr<int> b1;
|
||||
* b1 = new int; // BAD: can only assign another UniquePtr
|
||||
* int* ptr = b1; // BAD: no auto-conversion to pointer, use get()
|
||||
*
|
||||
* UniquePtr<int> b2(b1); // BAD: can't copy a UniquePtr
|
||||
* UniquePtr<int> b3 = b1; // BAD: can't copy-assign a UniquePtr
|
||||
*
|
||||
* (Note that changing a UniquePtr to store a direct |new| expression is
|
||||
* permitted, but usually you should use MakeUnique, defined at the end of this
|
||||
* header.)
|
||||
*
|
||||
* A few miscellaneous notes:
|
||||
*
|
||||
* UniquePtr, when not instantiated for an array type, can be move-constructed
|
||||
* and move-assigned, not only from itself but from "derived" UniquePtr<U, E>
|
||||
* instantiations where U converts to T and E converts to D. If you want to use
|
||||
* this, you're going to have to specify a deletion policy for both UniquePtr
|
||||
* instantations, and T pretty much has to have a virtual destructor. In other
|
||||
* words, this doesn't work:
|
||||
*
|
||||
* struct Base { virtual ~Base() {} };
|
||||
* struct Derived : Base {};
|
||||
*
|
||||
* UniquePtr<Base> b1;
|
||||
* // BAD: DefaultDelete<Base> and DefaultDelete<Derived> don't interconvert
|
||||
* UniquePtr<Derived> d1(Move(b));
|
||||
*
|
||||
* UniquePtr<Base> b2;
|
||||
* UniquePtr<Derived, DefaultDelete<Base>> d2(Move(b2)); // okay
|
||||
*
|
||||
* UniquePtr is specialized for array types. Specializing with an array type
|
||||
* creates a smart-pointer version of that array -- not a pointer to such an
|
||||
* array.
|
||||
*
|
||||
* UniquePtr<int[]> arr(new int[5]);
|
||||
* arr[0] = 4;
|
||||
*
|
||||
* What else is different? Deletion of course uses |delete[]|. An operator[]
|
||||
* is provided. Functionality that doesn't make sense for arrays is removed.
|
||||
* The constructors and mutating methods only accept array pointers (not T*, U*
|
||||
* that converts to T*, or UniquePtr<U[]> or UniquePtr<U>) or |nullptr|.
|
||||
*
|
||||
* It's perfectly okay for a function to return a UniquePtr. This transfers
|
||||
* the UniquePtr's sole ownership of the data, to the fresh UniquePtr created
|
||||
* in the calling function, that will then solely own that data. Such functions
|
||||
* can return a local variable UniquePtr, |nullptr|, |UniquePtr(ptr)| where
|
||||
* |ptr| is a |T*|, or a UniquePtr |Move()|'d from elsewhere.
|
||||
*
|
||||
* UniquePtr will commonly be a member of a class, with lifetime equivalent to
|
||||
* that of that class. If you want to expose the related resource, you could
|
||||
* expose a raw pointer via |get()|, but ownership of a raw pointer is
|
||||
* inherently unclear. So it's better to expose a |const UniquePtr&| instead.
|
||||
* This prohibits mutation but still allows use of |get()| when needed (but
|
||||
* operator-> is preferred). Of course, you can only use this smart pointer as
|
||||
* long as the enclosing class instance remains live -- no different than if you
|
||||
* exposed the |get()| raw pointer.
|
||||
*
|
||||
* To pass a UniquePtr-managed resource as a pointer, use a |const UniquePtr&|
|
||||
* argument. To specify an inout parameter (where the method may or may not
|
||||
* take ownership of the resource, or reset it), or to specify an out parameter
|
||||
* (where simply returning a |UniquePtr| isn't possible), use a |UniquePtr&|
|
||||
* argument. To unconditionally transfer ownership of a UniquePtr
|
||||
* into a method, use a |UniquePtr| argument. To conditionally transfer
|
||||
* ownership of a resource into a method, should the method want it, use a
|
||||
* |UniquePtr&&| argument.
|
||||
*/
|
||||
template<typename T, class D>
|
||||
class UniquePtr
|
||||
{
|
||||
public:
|
||||
typedef T ElementType;
|
||||
typedef D DeleterType;
|
||||
typedef typename detail::PointerType<T, DeleterType>::Type Pointer;
|
||||
|
||||
private:
|
||||
Pair<Pointer, DeleterType> mTuple;
|
||||
|
||||
Pointer& ptr() { return mTuple.first(); }
|
||||
const Pointer& ptr() const { return mTuple.first(); }
|
||||
|
||||
DeleterType& del() { return mTuple.second(); }
|
||||
const DeleterType& del() const { return mTuple.second(); }
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a UniquePtr containing |nullptr|.
|
||||
*/
|
||||
constexpr UniquePtr()
|
||||
: mTuple(static_cast<Pointer>(nullptr), DeleterType())
|
||||
{
|
||||
static_assert(!IsPointer<D>::value, "must provide a deleter instance");
|
||||
static_assert(!IsReference<D>::value, "must provide a deleter instance");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a UniquePtr containing |aPtr|.
|
||||
*/
|
||||
explicit UniquePtr(Pointer aPtr)
|
||||
: mTuple(aPtr, DeleterType())
|
||||
{
|
||||
static_assert(!IsPointer<D>::value, "must provide a deleter instance");
|
||||
static_assert(!IsReference<D>::value, "must provide a deleter instance");
|
||||
}
|
||||
|
||||
UniquePtr(Pointer aPtr,
|
||||
typename Conditional<IsReference<D>::value,
|
||||
D,
|
||||
const D&>::Type aD1)
|
||||
: mTuple(aPtr, aD1)
|
||||
{}
|
||||
|
||||
// If you encounter an error with MSVC10 about RemoveReference below, along
|
||||
// the lines that "more than one partial specialization matches the template
|
||||
// argument list": don't use UniquePtr<T, reference to function>! Ideally
|
||||
// you should make deletion use the same function every time, using a
|
||||
// deleter policy:
|
||||
//
|
||||
// // BAD, won't compile with MSVC10, deleter doesn't need to be a
|
||||
// // variable at all
|
||||
// typedef void (&FreeSignature)(void*);
|
||||
// UniquePtr<int, FreeSignature> ptr((int*) malloc(sizeof(int)), free);
|
||||
//
|
||||
// // GOOD, compiles with MSVC10, deletion behavior statically known and
|
||||
// // optimizable
|
||||
// struct DeleteByFreeing
|
||||
// {
|
||||
// void operator()(void* aPtr) { free(aPtr); }
|
||||
// };
|
||||
//
|
||||
// If deletion really, truly, must be a variable: you might be able to work
|
||||
// around this with a deleter class that contains the function reference.
|
||||
// But this workaround is untried and untested, because variable deletion
|
||||
// behavior really isn't something you should use.
|
||||
UniquePtr(Pointer aPtr,
|
||||
typename RemoveReference<D>::Type&& aD2)
|
||||
: mTuple(aPtr, Move(aD2))
|
||||
{
|
||||
static_assert(!IsReference<D>::value,
|
||||
"rvalue deleter can't be stored by reference");
|
||||
}
|
||||
|
||||
UniquePtr(UniquePtr&& aOther)
|
||||
: mTuple(aOther.release(), Forward<DeleterType>(aOther.get_deleter()))
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT
|
||||
UniquePtr(decltype(nullptr))
|
||||
: mTuple(nullptr, DeleterType())
|
||||
{
|
||||
static_assert(!IsPointer<D>::value, "must provide a deleter instance");
|
||||
static_assert(!IsReference<D>::value, "must provide a deleter instance");
|
||||
}
|
||||
|
||||
template<typename U, class E>
|
||||
MOZ_IMPLICIT
|
||||
UniquePtr(UniquePtr<U, E>&& aOther,
|
||||
typename EnableIf<IsConvertible<typename UniquePtr<U, E>::Pointer,
|
||||
Pointer>::value &&
|
||||
!IsArray<U>::value &&
|
||||
(IsReference<D>::value
|
||||
? IsSame<D, E>::value
|
||||
: IsConvertible<E, D>::value),
|
||||
int>::Type aDummy = 0)
|
||||
: mTuple(aOther.release(), Forward<E>(aOther.get_deleter()))
|
||||
{
|
||||
}
|
||||
|
||||
~UniquePtr() { reset(nullptr); }
|
||||
|
||||
UniquePtr& operator=(UniquePtr&& aOther)
|
||||
{
|
||||
reset(aOther.release());
|
||||
get_deleter() = Forward<DeleterType>(aOther.get_deleter());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U, typename E>
|
||||
UniquePtr& operator=(UniquePtr<U, E>&& aOther)
|
||||
{
|
||||
static_assert(IsConvertible<typename UniquePtr<U, E>::Pointer,
|
||||
Pointer>::value,
|
||||
"incompatible UniquePtr pointees");
|
||||
static_assert(!IsArray<U>::value,
|
||||
"can't assign from UniquePtr holding an array");
|
||||
|
||||
reset(aOther.release());
|
||||
get_deleter() = Forward<E>(aOther.get_deleter());
|
||||
return *this;
|
||||
}
|
||||
|
||||
UniquePtr& operator=(decltype(nullptr))
|
||||
{
|
||||
reset(nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const { return *get(); }
|
||||
Pointer operator->() const
|
||||
{
|
||||
MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr");
|
||||
return get();
|
||||
}
|
||||
|
||||
explicit operator bool() const { return get() != nullptr; }
|
||||
|
||||
Pointer get() const { return ptr(); }
|
||||
|
||||
DeleterType& get_deleter() { return del(); }
|
||||
const DeleterType& get_deleter() const { return del(); }
|
||||
|
||||
MOZ_MUST_USE Pointer release()
|
||||
{
|
||||
Pointer p = ptr();
|
||||
ptr() = nullptr;
|
||||
return p;
|
||||
}
|
||||
|
||||
void reset(Pointer aPtr = Pointer())
|
||||
{
|
||||
Pointer old = ptr();
|
||||
ptr() = aPtr;
|
||||
if (old != nullptr) {
|
||||
get_deleter()(old);
|
||||
}
|
||||
}
|
||||
|
||||
void swap(UniquePtr& aOther)
|
||||
{
|
||||
mTuple.swap(aOther.mTuple);
|
||||
}
|
||||
|
||||
UniquePtr(const UniquePtr& aOther) = delete; // construct using Move()!
|
||||
void operator=(const UniquePtr& aOther) = delete; // assign using Move()!
|
||||
};
|
||||
|
||||
// In case you didn't read the comment by the main definition (you should!): the
|
||||
// UniquePtr<T[]> specialization exists to manage array pointers. It deletes
|
||||
// such pointers using delete[], it will reject construction and modification
|
||||
// attempts using U* or U[]. Otherwise it works like the normal UniquePtr.
|
||||
template<typename T, class D>
|
||||
class UniquePtr<T[], D>
|
||||
{
|
||||
public:
|
||||
typedef T* Pointer;
|
||||
typedef T ElementType;
|
||||
typedef D DeleterType;
|
||||
|
||||
private:
|
||||
Pair<Pointer, DeleterType> mTuple;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a UniquePtr containing nullptr.
|
||||
*/
|
||||
constexpr UniquePtr()
|
||||
: mTuple(static_cast<Pointer>(nullptr), DeleterType())
|
||||
{
|
||||
static_assert(!IsPointer<D>::value, "must provide a deleter instance");
|
||||
static_assert(!IsReference<D>::value, "must provide a deleter instance");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a UniquePtr containing |aPtr|.
|
||||
*/
|
||||
explicit UniquePtr(Pointer aPtr)
|
||||
: mTuple(aPtr, DeleterType())
|
||||
{
|
||||
static_assert(!IsPointer<D>::value, "must provide a deleter instance");
|
||||
static_assert(!IsReference<D>::value, "must provide a deleter instance");
|
||||
}
|
||||
|
||||
// delete[] knows how to handle *only* an array of a single class type. For
|
||||
// delete[] to work correctly, it must know the size of each element, the
|
||||
// fields and base classes of each element requiring destruction, and so on.
|
||||
// So forbid all overloads which would end up invoking delete[] on a pointer
|
||||
// of the wrong type.
|
||||
template<typename U>
|
||||
UniquePtr(U&& aU,
|
||||
typename EnableIf<IsPointer<U>::value &&
|
||||
IsConvertible<U, Pointer>::value,
|
||||
int>::Type aDummy = 0)
|
||||
= delete;
|
||||
|
||||
UniquePtr(Pointer aPtr,
|
||||
typename Conditional<IsReference<D>::value,
|
||||
D,
|
||||
const D&>::Type aD1)
|
||||
: mTuple(aPtr, aD1)
|
||||
{}
|
||||
|
||||
// If you encounter an error with MSVC10 about RemoveReference below, along
|
||||
// the lines that "more than one partial specialization matches the template
|
||||
// argument list": don't use UniquePtr<T[], reference to function>! See the
|
||||
// comment by this constructor in the non-T[] specialization above.
|
||||
UniquePtr(Pointer aPtr,
|
||||
typename RemoveReference<D>::Type&& aD2)
|
||||
: mTuple(aPtr, Move(aD2))
|
||||
{
|
||||
static_assert(!IsReference<D>::value,
|
||||
"rvalue deleter can't be stored by reference");
|
||||
}
|
||||
|
||||
// Forbidden for the same reasons as stated above.
|
||||
template<typename U, typename V>
|
||||
UniquePtr(U&& aU, V&& aV,
|
||||
typename EnableIf<IsPointer<U>::value &&
|
||||
IsConvertible<U, Pointer>::value,
|
||||
int>::Type aDummy = 0)
|
||||
= delete;
|
||||
|
||||
UniquePtr(UniquePtr&& aOther)
|
||||
: mTuple(aOther.release(), Forward<DeleterType>(aOther.get_deleter()))
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT
|
||||
UniquePtr(decltype(nullptr))
|
||||
: mTuple(nullptr, DeleterType())
|
||||
{
|
||||
static_assert(!IsPointer<D>::value, "must provide a deleter instance");
|
||||
static_assert(!IsReference<D>::value, "must provide a deleter instance");
|
||||
}
|
||||
|
||||
~UniquePtr() { reset(nullptr); }
|
||||
|
||||
UniquePtr& operator=(UniquePtr&& aOther)
|
||||
{
|
||||
reset(aOther.release());
|
||||
get_deleter() = Forward<DeleterType>(aOther.get_deleter());
|
||||
return *this;
|
||||
}
|
||||
|
||||
UniquePtr& operator=(decltype(nullptr))
|
||||
{
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return get() != nullptr; }
|
||||
|
||||
T& operator[](decltype(sizeof(int)) aIndex) const { return get()[aIndex]; }
|
||||
Pointer get() const { return mTuple.first(); }
|
||||
|
||||
DeleterType& get_deleter() { return mTuple.second(); }
|
||||
const DeleterType& get_deleter() const { return mTuple.second(); }
|
||||
|
||||
MOZ_MUST_USE Pointer release()
|
||||
{
|
||||
Pointer p = mTuple.first();
|
||||
mTuple.first() = nullptr;
|
||||
return p;
|
||||
}
|
||||
|
||||
void reset(Pointer aPtr = Pointer())
|
||||
{
|
||||
Pointer old = mTuple.first();
|
||||
mTuple.first() = aPtr;
|
||||
if (old != nullptr) {
|
||||
mTuple.second()(old);
|
||||
}
|
||||
}
|
||||
|
||||
void reset(decltype(nullptr))
|
||||
{
|
||||
Pointer old = mTuple.first();
|
||||
mTuple.first() = nullptr;
|
||||
if (old != nullptr) {
|
||||
mTuple.second()(old);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void reset(U) = delete;
|
||||
|
||||
void swap(UniquePtr& aOther) { mTuple.swap(aOther.mTuple); }
|
||||
|
||||
UniquePtr(const UniquePtr& aOther) = delete; // construct using Move()!
|
||||
void operator=(const UniquePtr& aOther) = delete; // assign using Move()!
|
||||
};
|
||||
|
||||
/**
|
||||
* A default deletion policy using plain old operator delete.
|
||||
*
|
||||
* Note that this type can be specialized, but authors should beware of the risk
|
||||
* that the specialization may at some point cease to match (either because it
|
||||
* gets moved to a different compilation unit or the signature changes). If the
|
||||
* non-specialized (|delete|-based) version compiles for that type but does the
|
||||
* wrong thing, bad things could happen.
|
||||
*
|
||||
* This is a non-issue for types which are always incomplete (i.e. opaque handle
|
||||
* types), since |delete|-ing such a type will always trigger a compilation
|
||||
* error.
|
||||
*/
|
||||
template<typename T>
|
||||
class DefaultDelete
|
||||
{
|
||||
public:
|
||||
constexpr DefaultDelete() {}
|
||||
|
||||
template<typename U>
|
||||
MOZ_IMPLICIT DefaultDelete(const DefaultDelete<U>& aOther,
|
||||
typename EnableIf<mozilla::IsConvertible<U*, T*>::value,
|
||||
int>::Type aDummy = 0)
|
||||
{}
|
||||
|
||||
void operator()(T* aPtr) const
|
||||
{
|
||||
static_assert(sizeof(T) > 0, "T must be complete");
|
||||
delete aPtr;
|
||||
}
|
||||
};
|
||||
|
||||
/** A default deletion policy using operator delete[]. */
|
||||
template<typename T>
|
||||
class DefaultDelete<T[]>
|
||||
{
|
||||
public:
|
||||
constexpr DefaultDelete() {}
|
||||
|
||||
void operator()(T* aPtr) const
|
||||
{
|
||||
static_assert(sizeof(T) > 0, "T must be complete");
|
||||
delete[] aPtr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void operator()(U* aPtr) const = delete;
|
||||
};
|
||||
|
||||
template<typename T, class D>
|
||||
void
|
||||
Swap(UniquePtr<T, D>& aX, UniquePtr<T, D>& aY)
|
||||
{
|
||||
aX.swap(aY);
|
||||
}
|
||||
|
||||
template<typename T, class D, typename U, class E>
|
||||
bool
|
||||
operator==(const UniquePtr<T, D>& aX, const UniquePtr<U, E>& aY)
|
||||
{
|
||||
return aX.get() == aY.get();
|
||||
}
|
||||
|
||||
template<typename T, class D, typename U, class E>
|
||||
bool
|
||||
operator!=(const UniquePtr<T, D>& aX, const UniquePtr<U, E>& aY)
|
||||
{
|
||||
return aX.get() != aY.get();
|
||||
}
|
||||
|
||||
template<typename T, class D>
|
||||
bool
|
||||
operator==(const UniquePtr<T, D>& aX, decltype(nullptr))
|
||||
{
|
||||
return !aX;
|
||||
}
|
||||
|
||||
template<typename T, class D>
|
||||
bool
|
||||
operator==(decltype(nullptr), const UniquePtr<T, D>& aX)
|
||||
{
|
||||
return !aX;
|
||||
}
|
||||
|
||||
template<typename T, class D>
|
||||
bool
|
||||
operator!=(const UniquePtr<T, D>& aX, decltype(nullptr))
|
||||
{
|
||||
return bool(aX);
|
||||
}
|
||||
|
||||
template<typename T, class D>
|
||||
bool
|
||||
operator!=(decltype(nullptr), const UniquePtr<T, D>& aX)
|
||||
{
|
||||
return bool(aX);
|
||||
}
|
||||
|
||||
// No operator<, operator>, operator<=, operator>= for now because simplicity.
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
struct UniqueSelector
|
||||
{
|
||||
typedef UniquePtr<T> SingleObject;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct UniqueSelector<T[]>
|
||||
{
|
||||
typedef UniquePtr<T[]> UnknownBound;
|
||||
};
|
||||
|
||||
template<typename T, decltype(sizeof(int)) N>
|
||||
struct UniqueSelector<T[N]>
|
||||
{
|
||||
typedef UniquePtr<T[N]> KnownBound;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* MakeUnique is a helper function for allocating new'd objects and arrays,
|
||||
* returning a UniquePtr containing the resulting pointer. The semantics of
|
||||
* MakeUnique<Type>(...) are as follows.
|
||||
*
|
||||
* If Type is an array T[n]:
|
||||
* Disallowed, deleted, no overload for you!
|
||||
* If Type is an array T[]:
|
||||
* MakeUnique<T[]>(size_t) is the only valid overload. The pointer returned
|
||||
* is as if by |new T[n]()|, which value-initializes each element. (If T
|
||||
* isn't a class type, this will zero each element. If T is a class type,
|
||||
* then roughly speaking, each element will be constructed using its default
|
||||
* constructor. See C++11 [dcl.init]p7 for the full gory details.)
|
||||
* If Type is non-array T:
|
||||
* The arguments passed to MakeUnique<T>(...) are forwarded into a
|
||||
* |new T(...)| call, initializing the T as would happen if executing
|
||||
* |T(...)|.
|
||||
*
|
||||
* There are various benefits to using MakeUnique instead of |new| expressions.
|
||||
*
|
||||
* First, MakeUnique eliminates use of |new| from code entirely. If objects are
|
||||
* only created through UniquePtr, then (assuming all explicit release() calls
|
||||
* are safe, including transitively, and no type-safety casting funniness)
|
||||
* correctly maintained ownership of the UniquePtr guarantees no leaks are
|
||||
* possible. (This pays off best if a class is only ever created through a
|
||||
* factory method on the class, using a private constructor.)
|
||||
*
|
||||
* Second, initializing a UniquePtr using a |new| expression requires repeating
|
||||
* the name of the new'd type, whereas MakeUnique in concert with the |auto|
|
||||
* keyword names it only once:
|
||||
*
|
||||
* UniquePtr<char> ptr1(new char()); // repetitive
|
||||
* auto ptr2 = MakeUnique<char>(); // shorter
|
||||
*
|
||||
* Of course this assumes the reader understands the operation MakeUnique
|
||||
* performs. In the long run this is probably a reasonable assumption. In the
|
||||
* short run you'll have to use your judgment about what readers can be expected
|
||||
* to know, or to quickly look up.
|
||||
*
|
||||
* Third, a call to MakeUnique can be assigned directly to a UniquePtr. In
|
||||
* contrast you can't assign a pointer into a UniquePtr without using the
|
||||
* cumbersome reset().
|
||||
*
|
||||
* UniquePtr<char> p;
|
||||
* p = new char; // ERROR
|
||||
* p.reset(new char); // works, but fugly
|
||||
* p = MakeUnique<char>(); // preferred
|
||||
*
|
||||
* (And third, although not relevant to Mozilla: MakeUnique is exception-safe.
|
||||
* An exception thrown after |new T| succeeds will leak that memory, unless the
|
||||
* pointer is assigned to an object that will manage its ownership. UniquePtr
|
||||
* ably serves this function.)
|
||||
*/
|
||||
|
||||
template<typename T, typename... Args>
|
||||
typename detail::UniqueSelector<T>::SingleObject
|
||||
MakeUnique(Args&&... aArgs)
|
||||
{
|
||||
return UniquePtr<T>(new T(Forward<Args>(aArgs)...));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename detail::UniqueSelector<T>::UnknownBound
|
||||
MakeUnique(decltype(sizeof(int)) aN)
|
||||
{
|
||||
typedef typename RemoveExtent<T>::Type ArrayType;
|
||||
return UniquePtr<T>(new ArrayType[aN]());
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
typename detail::UniqueSelector<T>::KnownBound
|
||||
MakeUnique(Args&&... aArgs) = delete;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_UniquePtr_h */
|
||||
Loading…
Reference in New Issue