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