gecko-dev/js/public/Value.h

1952 lines
52 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
/* JS::Value implementation. */
#ifndef js_Value_h
#define js_Value_h
#include "mozilla/Attributes.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
#include <limits> /* for std::numeric_limits */
#include "js-config.h"
#include "jstypes.h"
#include "js/GCAPI.h"
#include "js/RootingAPI.h"
#include "js/Utility.h"
namespace JS { class Value; }
/* JS::Value can store a full int32_t. */
#define JSVAL_INT_BITS 32
#define JSVAL_INT_MIN ((int32_t)0x80000000)
#define JSVAL_INT_MAX ((int32_t)0x7fffffff)
/*
* Try to get jsvals 64-bit aligned. We could almost assert that all values are
* aligned, but MSVC and GCC occasionally break alignment.
*/
#if defined(__GNUC__) || defined(__xlc__) || defined(__xlC__)
# define JSVAL_ALIGNMENT __attribute__((aligned (8)))
#elif defined(_MSC_VER)
/*
* Structs can be aligned with MSVC, but not if they are used as parameters,
* so we just don't try to align.
*/
# define JSVAL_ALIGNMENT
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
# define JSVAL_ALIGNMENT
#elif defined(__HP_cc) || defined(__HP_aCC)
# define JSVAL_ALIGNMENT
#endif
#if defined(JS_PUNBOX64)
# define JSVAL_TAG_SHIFT 47
#endif
/*
* We try to use enums so that printing a jsval_layout in the debugger shows
* nice symbolic type tags, however we can only do this when we can force the
* underlying type of the enum to be the desired size.
*/
#if !defined(__SUNPRO_CC) && !defined(__xlC__)
#if defined(_MSC_VER)
# define JS_ENUM_HEADER(id, type) enum id : type
# define JS_ENUM_FOOTER(id)
#else
# define JS_ENUM_HEADER(id, type) enum id
# define JS_ENUM_FOOTER(id) __attribute__((packed))
#endif
/* Remember to propagate changes to the C defines below. */
JS_ENUM_HEADER(JSValueType, uint8_t)
{
JSVAL_TYPE_DOUBLE = 0x00,
JSVAL_TYPE_INT32 = 0x01,
JSVAL_TYPE_UNDEFINED = 0x02,
JSVAL_TYPE_BOOLEAN = 0x03,
JSVAL_TYPE_MAGIC = 0x04,
JSVAL_TYPE_STRING = 0x05,
JSVAL_TYPE_SYMBOL = 0x06,
JSVAL_TYPE_NULL = 0x07,
JSVAL_TYPE_OBJECT = 0x08,
/* These never appear in a jsval; they are only provided as an out-of-band value. */
JSVAL_TYPE_UNKNOWN = 0x20,
JSVAL_TYPE_MISSING = 0x21
} JS_ENUM_FOOTER(JSValueType);
static_assert(sizeof(JSValueType) == 1,
"compiler typed enum support is apparently buggy");
#if defined(JS_NUNBOX32)
/* Remember to propagate changes to the C defines below. */
JS_ENUM_HEADER(JSValueTag, uint32_t)
{
JSVAL_TAG_CLEAR = 0xFFFFFF80,
JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED,
JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
JSVAL_TAG_SYMBOL = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT
} JS_ENUM_FOOTER(JSValueTag);
static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
"compiler typed enum support is apparently buggy");
#elif defined(JS_PUNBOX64)
/* Remember to propagate changes to the C defines below. */
JS_ENUM_HEADER(JSValueTag, uint32_t)
{
JSVAL_TAG_MAX_DOUBLE = 0x1FFF0,
JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
JSVAL_TAG_UNDEFINED = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED,
JSVAL_TAG_STRING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
JSVAL_TAG_SYMBOL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
JSVAL_TAG_BOOLEAN = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
JSVAL_TAG_MAGIC = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
JSVAL_TAG_NULL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT
} JS_ENUM_FOOTER(JSValueTag);
static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
"compiler typed enum support is apparently buggy");
JS_ENUM_HEADER(JSValueShiftedTag, uint64_t)
{
JSVAL_SHIFTED_TAG_MAX_DOUBLE = ((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF),
JSVAL_SHIFTED_TAG_INT32 = (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_UNDEFINED = (((uint64_t)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_STRING = (((uint64_t)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_SYMBOL = (((uint64_t)JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_BOOLEAN = (((uint64_t)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_MAGIC = (((uint64_t)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_NULL = (((uint64_t)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_OBJECT = (((uint64_t)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT)
} JS_ENUM_FOOTER(JSValueShiftedTag);
static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
"compiler typed enum support is apparently buggy");
#endif
/*
* All our supported compilers implement C++11 |enum Foo : T| syntax, so don't
* expose these macros. (This macro exists *only* because gcc bug 51242
* <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51242> makes bit-fields of
* typed enums trigger a warning that can't be turned off. Don't expose it
* beyond this file!)
*/
#undef JS_ENUM_HEADER
#undef JS_ENUM_FOOTER
#else /* !defined(__SUNPRO_CC) && !defined(__xlC__) */
typedef uint8_t JSValueType;
#define JSVAL_TYPE_DOUBLE ((uint8_t)0x00)
#define JSVAL_TYPE_INT32 ((uint8_t)0x01)
#define JSVAL_TYPE_UNDEFINED ((uint8_t)0x02)
#define JSVAL_TYPE_BOOLEAN ((uint8_t)0x03)
#define JSVAL_TYPE_MAGIC ((uint8_t)0x04)
#define JSVAL_TYPE_STRING ((uint8_t)0x05)
#define JSVAL_TYPE_SYMBOL ((uint8_t)0x06)
#define JSVAL_TYPE_NULL ((uint8_t)0x07)
#define JSVAL_TYPE_OBJECT ((uint8_t)0x08)
#define JSVAL_TYPE_UNKNOWN ((uint8_t)0x20)
#if defined(JS_NUNBOX32)
typedef uint32_t JSValueTag;
#define JSVAL_TAG_CLEAR ((uint32_t)(0xFFFFFF80))
#define JSVAL_TAG_INT32 ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32))
#define JSVAL_TAG_UNDEFINED ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED))
#define JSVAL_TAG_STRING ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING))
#define JSVAL_TAG_SYMBOL ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL))
#define JSVAL_TAG_BOOLEAN ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN))
#define JSVAL_TAG_MAGIC ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC))
#define JSVAL_TAG_NULL ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL))
#define JSVAL_TAG_OBJECT ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT))
#elif defined(JS_PUNBOX64)
typedef uint32_t JSValueTag;
#define JSVAL_TAG_MAX_DOUBLE ((uint32_t)(0x1FFF0))
#define JSVAL_TAG_INT32 (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32)
#define JSVAL_TAG_UNDEFINED (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED)
#define JSVAL_TAG_STRING (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING)
#define JSVAL_TAG_SYMBOL (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL)
#define JSVAL_TAG_BOOLEAN (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN)
#define JSVAL_TAG_MAGIC (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC)
#define JSVAL_TAG_NULL (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL)
#define JSVAL_TAG_OBJECT (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT)
typedef uint64_t JSValueShiftedTag;
#define JSVAL_SHIFTED_TAG_MAX_DOUBLE ((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF)
#define JSVAL_SHIFTED_TAG_INT32 (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT)
#define JSVAL_SHIFTED_TAG_UNDEFINED (((uint64_t)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT)
#define JSVAL_SHIFTED_TAG_STRING (((uint64_t)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT)
#define JSVAL_SHIFTED_TAG_SYMBOL (((uint64_t)JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT)
#define JSVAL_SHIFTED_TAG_BOOLEAN (((uint64_t)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT)
#define JSVAL_SHIFTED_TAG_MAGIC (((uint64_t)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT)
#define JSVAL_SHIFTED_TAG_NULL (((uint64_t)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT)
#define JSVAL_SHIFTED_TAG_OBJECT (((uint64_t)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT)
#endif /* JS_PUNBOX64 */
#endif /* !defined(__SUNPRO_CC) && !defined(__xlC__) */
#if defined(JS_NUNBOX32)
#define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_CLEAR | (type)))
#define JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET JSVAL_TAG_NULL
#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32
#define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET JSVAL_TAG_STRING
#elif defined(JS_PUNBOX64)
#define JSVAL_PAYLOAD_MASK 0x00007FFFFFFFFFFFLL
#define JSVAL_TAG_MASK 0xFFFF800000000000LL
#define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type)))
#define JSVAL_TYPE_TO_SHIFTED_TAG(type) (((uint64_t)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT)
#define JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET JSVAL_TAG_NULL
#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32
#define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET JSVAL_TAG_STRING
#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET JSVAL_SHIFTED_TAG_NULL
#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET JSVAL_SHIFTED_TAG_OBJECT
#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET JSVAL_SHIFTED_TAG_UNDEFINED
#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET JSVAL_SHIFTED_TAG_STRING
#endif /* JS_PUNBOX64 */
typedef enum JSWhyMagic
{
/** a hole in a native object's elements */
JS_ELEMENTS_HOLE,
/** there is not a pending iterator value */
JS_NO_ITER_VALUE,
/** exception value thrown when closing a generator */
JS_GENERATOR_CLOSING,
/** compiler sentinel value */
JS_NO_CONSTANT,
/** used in debug builds to catch tracing errors */
JS_THIS_POISON,
/** used in debug builds to catch tracing errors */
JS_ARG_POISON,
/** an empty subnode in the AST serializer */
JS_SERIALIZE_NO_NODE,
/** lazy arguments value on the stack */
JS_LAZY_ARGUMENTS,
/** optimized-away 'arguments' value */
JS_OPTIMIZED_ARGUMENTS,
/** magic value passed to natives to indicate construction */
JS_IS_CONSTRUCTING,
/** arguments.callee has been overwritten */
JS_OVERWRITTEN_CALLEE,
/** value of static block object slot */
JS_BLOCK_NEEDS_CLONE,
/** see class js::HashableValue */
JS_HASH_KEY_EMPTY,
/** error while running Ion code */
JS_ION_ERROR,
/** missing recover instruction result */
JS_ION_BAILOUT,
/** optimized out slot */
JS_OPTIMIZED_OUT,
/** uninitialized lexical bindings that produce ReferenceError on touch. */
JS_UNINITIALIZED_LEXICAL,
/** for local use */
JS_GENERIC_MAGIC,
JS_WHY_MAGIC_COUNT
} JSWhyMagic;
#if defined(IS_LITTLE_ENDIAN)
# if defined(JS_NUNBOX32)
typedef union jsval_layout
{
uint64_t asBits;
struct {
union {
int32_t i32;
uint32_t u32;
uint32_t boo; // Don't use |bool| -- it must be four bytes.
JSString* str;
JS::Symbol* sym;
JSObject* obj;
js::gc::Cell* cell;
void* ptr;
JSWhyMagic why;
size_t word;
uintptr_t uintptr;
} payload;
JSValueTag tag;
} s;
double asDouble;
void* asPtr;
} JSVAL_ALIGNMENT jsval_layout;
# elif defined(JS_PUNBOX64)
typedef union jsval_layout
{
uint64_t asBits;
#if !defined(_WIN64)
/* MSVC does not pack these correctly :-( */
struct {
uint64_t payload47 : 47;
JSValueTag tag : 17;
} debugView;
#endif
struct {
union {
int32_t i32;
uint32_t u32;
JSWhyMagic why;
} payload;
} s;
double asDouble;
void* asPtr;
size_t asWord;
uintptr_t asUIntPtr;
} JSVAL_ALIGNMENT jsval_layout;
# endif /* JS_PUNBOX64 */
#else /* defined(IS_LITTLE_ENDIAN) */
# if defined(JS_NUNBOX32)
typedef union jsval_layout
{
uint64_t asBits;
struct {
JSValueTag tag;
union {
int32_t i32;
uint32_t u32;
uint32_t boo; // Don't use |bool| -- it must be four bytes.
JSString* str;
JS::Symbol* sym;
JSObject* obj;
js::gc::Cell* cell;
void* ptr;
JSWhyMagic why;
size_t word;
uintptr_t uintptr;
} payload;
} s;
double asDouble;
void* asPtr;
} JSVAL_ALIGNMENT jsval_layout;
# elif defined(JS_PUNBOX64)
typedef union jsval_layout
{
uint64_t asBits;
struct {
JSValueTag tag : 17;
uint64_t payload47 : 47;
} debugView;
struct {
uint32_t padding;
union {
int32_t i32;
uint32_t u32;
JSWhyMagic why;
} payload;
} s;
double asDouble;
void* asPtr;
size_t asWord;
uintptr_t asUIntPtr;
} JSVAL_ALIGNMENT jsval_layout;
# endif /* JS_PUNBOX64 */
#endif /* defined(IS_LITTLE_ENDIAN) */
JS_STATIC_ASSERT(sizeof(jsval_layout) == 8);
/*
* For codesize purposes on some platforms, it's important that the
* compiler know that JS::Values constructed from constant values can be
* folded to constant bit patterns at compile time, rather than
* constructed at runtime. Doing this requires a fair amount of C++11
* features, which are not supported on all of our compilers. Set up
* some defines and helper macros in an attempt to confine the ugliness
* here, rather than scattering it all about the file. The important
* features are:
*
* - constexpr;
* - defaulted functions;
* - C99-style designated initializers.
*/
#if defined(__clang__)
# if __has_feature(cxx_constexpr) && __has_feature(cxx_defaulted_functions)
# define JS_VALUE_IS_CONSTEXPR
# endif
#elif defined(__GNUC__)
/*
* We need 4.5 for defaulted functions, 4.6 for constexpr, 4.7 because 4.6
* doesn't understand |(X) { .field = ... }| syntax, and 4.7.3 because
* versions prior to that have bugs in the C++ front-end that cause crashes.
*/
# if MOZ_GCC_VERSION_AT_LEAST(4, 7, 3)
# define JS_VALUE_IS_CONSTEXPR
# endif
#endif
#if defined(JS_VALUE_IS_CONSTEXPR)
# define JS_RETURN_LAYOUT_FROM_BITS(BITS) \
return (jsval_layout) { .asBits = (BITS) }
# define JS_VALUE_CONSTEXPR MOZ_CONSTEXPR
# define JS_VALUE_CONSTEXPR_VAR MOZ_CONSTEXPR_VAR
#else
# define JS_RETURN_LAYOUT_FROM_BITS(BITS) \
jsval_layout l; \
l.asBits = (BITS); \
return l;
# define JS_VALUE_CONSTEXPR
# define JS_VALUE_CONSTEXPR_VAR const
#endif
#if defined(JS_NUNBOX32)
/*
* N.B. GCC, in some but not all cases, chooses to emit signed comparison of
* JSValueTag even though its underlying type has been forced to be uint32_t.
* Thus, all comparisons should explicitly cast operands to uint32_t.
*/
static inline JS_VALUE_CONSTEXPR jsval_layout
BUILD_JSVAL(JSValueTag tag, uint32_t payload)
{
JS_RETURN_LAYOUT_FROM_BITS((((uint64_t)(uint32_t)tag) << 32) | payload);
}
static inline bool
JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
{
return (uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_CLEAR;
}
static inline jsval_layout
DOUBLE_TO_JSVAL_IMPL(double d)
{
jsval_layout l;
l.asDouble = d;
MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
return l;
}
static inline bool
JSVAL_IS_INT32_IMPL(jsval_layout l)
{
return l.s.tag == JSVAL_TAG_INT32;
}
static inline int32_t
JSVAL_TO_INT32_IMPL(jsval_layout l)
{
return l.s.payload.i32;
}
static inline JS_VALUE_CONSTEXPR jsval_layout
INT32_TO_JSVAL_IMPL(int32_t i)
{
#if defined(JS_VALUE_IS_CONSTEXPR)
return BUILD_JSVAL(JSVAL_TAG_INT32, i);
#else
jsval_layout l;
l.s.tag = JSVAL_TAG_INT32;
l.s.payload.i32 = i;
return l;
#endif
}
static inline bool
JSVAL_IS_NUMBER_IMPL(jsval_layout l)
{
JSValueTag tag = l.s.tag;
MOZ_ASSERT(tag != JSVAL_TAG_CLEAR);
return (uint32_t)tag <= (uint32_t)JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET;
}
static inline bool
JSVAL_IS_UNDEFINED_IMPL(jsval_layout l)
{
return l.s.tag == JSVAL_TAG_UNDEFINED;
}
static inline bool
JSVAL_IS_STRING_IMPL(jsval_layout l)
{
return l.s.tag == JSVAL_TAG_STRING;
}
static inline jsval_layout
STRING_TO_JSVAL_IMPL(JSString* str)
{
jsval_layout l;
MOZ_ASSERT(uintptr_t(str) > 0x1000);
l.s.tag = JSVAL_TAG_STRING;
l.s.payload.str = str;
return l;
}
static inline JSString*
JSVAL_TO_STRING_IMPL(jsval_layout l)
{
return l.s.payload.str;
}
static inline bool
JSVAL_IS_SYMBOL_IMPL(jsval_layout l)
{
return l.s.tag == JSVAL_TAG_SYMBOL;
}
static inline jsval_layout
SYMBOL_TO_JSVAL_IMPL(JS::Symbol* sym)
{
jsval_layout l;
MOZ_ASSERT(uintptr_t(sym) > 0x1000);
l.s.tag = JSVAL_TAG_SYMBOL;
l.s.payload.sym = sym;
return l;
}
static inline JS::Symbol*
JSVAL_TO_SYMBOL_IMPL(jsval_layout l)
{
return l.s.payload.sym;
}
static inline bool
JSVAL_IS_BOOLEAN_IMPL(jsval_layout l)
{
return l.s.tag == JSVAL_TAG_BOOLEAN;
}
static inline bool
JSVAL_TO_BOOLEAN_IMPL(jsval_layout l)
{
return l.s.payload.boo;
}
static inline jsval_layout
BOOLEAN_TO_JSVAL_IMPL(bool b)
{
jsval_layout l;
l.s.tag = JSVAL_TAG_BOOLEAN;
l.s.payload.boo = b;
return l;
}
static inline bool
JSVAL_IS_MAGIC_IMPL(jsval_layout l)
{
return l.s.tag == JSVAL_TAG_MAGIC;
}
static inline bool
JSVAL_IS_OBJECT_IMPL(jsval_layout l)
{
return l.s.tag == JSVAL_TAG_OBJECT;
}
static inline bool
JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l)
{
return (uint32_t)l.s.tag < (uint32_t)JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET;
}
static inline bool
JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l)
{
MOZ_ASSERT((uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_OBJECT);
return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET;
}
static inline JSObject*
JSVAL_TO_OBJECT_IMPL(jsval_layout l)
{
return l.s.payload.obj;
}
static inline jsval_layout
OBJECT_TO_JSVAL_IMPL(JSObject* obj)
{
jsval_layout l;
MOZ_ASSERT(uintptr_t(obj) > 0x1000 || uintptr_t(obj) == 0x42);
l.s.tag = JSVAL_TAG_OBJECT;
l.s.payload.obj = obj;
return l;
}
static inline bool
JSVAL_IS_NULL_IMPL(jsval_layout l)
{
return l.s.tag == JSVAL_TAG_NULL;
}
static inline jsval_layout
PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
{
jsval_layout l;
MOZ_ASSERT(((uint32_t)ptr & 1) == 0);
l.s.tag = (JSValueTag)0;
l.s.payload.ptr = ptr;
MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
return l;
}
static inline void*
JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
{
return l.s.payload.ptr;
}
static inline bool
JSVAL_IS_GCTHING_IMPL(jsval_layout l)
{
/* gcc sometimes generates signed < without explicit casts. */
return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET;
}
static inline js::gc::Cell*
JSVAL_TO_GCTHING_IMPL(jsval_layout l)
{
return l.s.payload.cell;
}
static inline uint32_t
JSVAL_TRACE_KIND_IMPL(jsval_layout l)
{
static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
"Value type tags must correspond with JS::TraceKinds.");
return l.s.tag & 0x03;
}
static inline bool
JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32)
{
return l.s.tag == JSVAL_TAG_INT32 && l.s.payload.i32 == i32;
}
static inline bool
JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(jsval_layout l, bool b)
{
return (l.s.tag == JSVAL_TAG_BOOLEAN) && (l.s.payload.boo == uint32_t(b));
}
static inline jsval_layout
MAGIC_TO_JSVAL_IMPL(JSWhyMagic why)
{
jsval_layout l;
l.s.tag = JSVAL_TAG_MAGIC;
l.s.payload.why = why;
return l;
}
static inline jsval_layout
MAGIC_UINT32_TO_JSVAL_IMPL(uint32_t payload)
{
jsval_layout l;
l.s.tag = JSVAL_TAG_MAGIC;
l.s.payload.u32 = payload;
return l;
}
static inline bool
JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs)
{
JSValueTag ltag = lhs.s.tag, rtag = rhs.s.tag;
return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR);
}
static inline JSValueType
JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l)
{
uint32_t type = l.s.tag & 0xF;
MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
return (JSValueType)type;
}
#elif defined(JS_PUNBOX64)
static inline JS_VALUE_CONSTEXPR jsval_layout
BUILD_JSVAL(JSValueTag tag, uint64_t payload)
{
JS_RETURN_LAYOUT_FROM_BITS((((uint64_t)(uint32_t)tag) << JSVAL_TAG_SHIFT) | payload);
}
static inline bool
JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
{
return l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
}
static inline jsval_layout
DOUBLE_TO_JSVAL_IMPL(double d)
{
jsval_layout l;
l.asDouble = d;
MOZ_ASSERT(l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE);
return l;
}
static inline bool
JSVAL_IS_INT32_IMPL(jsval_layout l)
{
return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_INT32;
}
static inline int32_t
JSVAL_TO_INT32_IMPL(jsval_layout l)
{
return (int32_t)l.asBits;
}
static inline JS_VALUE_CONSTEXPR jsval_layout
INT32_TO_JSVAL_IMPL(int32_t i32)
{
JS_RETURN_LAYOUT_FROM_BITS(((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32);
}
static inline bool
JSVAL_IS_NUMBER_IMPL(jsval_layout l)
{
return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET;
}
static inline bool
JSVAL_IS_UNDEFINED_IMPL(jsval_layout l)
{
return l.asBits == JSVAL_SHIFTED_TAG_UNDEFINED;
}
static inline bool
JSVAL_IS_STRING_IMPL(jsval_layout l)
{
return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_STRING;
}
static inline jsval_layout
STRING_TO_JSVAL_IMPL(JSString* str)
{
jsval_layout l;
uint64_t strBits = (uint64_t)str;
MOZ_ASSERT(uintptr_t(str) > 0x1000);
MOZ_ASSERT((strBits >> JSVAL_TAG_SHIFT) == 0);
l.asBits = strBits | JSVAL_SHIFTED_TAG_STRING;
return l;
}
static inline JSString*
JSVAL_TO_STRING_IMPL(jsval_layout l)
{
return (JSString*)(l.asBits & JSVAL_PAYLOAD_MASK);
}
static inline bool
JSVAL_IS_SYMBOL_IMPL(jsval_layout l)
{
return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_SYMBOL;
}
static inline jsval_layout
SYMBOL_TO_JSVAL_IMPL(JS::Symbol* sym)
{
jsval_layout l;
uint64_t symBits = (uint64_t)sym;
MOZ_ASSERT(uintptr_t(sym) > 0x1000);
MOZ_ASSERT((symBits >> JSVAL_TAG_SHIFT) == 0);
l.asBits = symBits | JSVAL_SHIFTED_TAG_SYMBOL;
return l;
}
static inline JS::Symbol*
JSVAL_TO_SYMBOL_IMPL(jsval_layout l)
{
return (JS::Symbol*)(l.asBits & JSVAL_PAYLOAD_MASK);
}
static inline bool
JSVAL_IS_BOOLEAN_IMPL(jsval_layout l)
{
return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_BOOLEAN;
}
static inline bool
JSVAL_TO_BOOLEAN_IMPL(jsval_layout l)
{
return (bool)(l.asBits & JSVAL_PAYLOAD_MASK);
}
static inline jsval_layout
BOOLEAN_TO_JSVAL_IMPL(bool b)
{
jsval_layout l;
l.asBits = ((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN;
return l;
}
static inline bool
JSVAL_IS_MAGIC_IMPL(jsval_layout l)
{
return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_MAGIC;
}
static inline bool
JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l)
{
return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET;
}
static inline bool
JSVAL_IS_OBJECT_IMPL(jsval_layout l)
{
MOZ_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
return l.asBits >= JSVAL_SHIFTED_TAG_OBJECT;
}
static inline bool
JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l)
{
MOZ_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET;
}
static inline JSObject*
JSVAL_TO_OBJECT_IMPL(jsval_layout l)
{
uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK;
MOZ_ASSERT((ptrBits & 0x7) == 0);
return (JSObject*)ptrBits;
}
static inline jsval_layout
OBJECT_TO_JSVAL_IMPL(JSObject* obj)
{
jsval_layout l;
uint64_t objBits = (uint64_t)obj;
MOZ_ASSERT(uintptr_t(obj) > 0x1000 || uintptr_t(obj) == 0x42);
MOZ_ASSERT((objBits >> JSVAL_TAG_SHIFT) == 0);
l.asBits = objBits | JSVAL_SHIFTED_TAG_OBJECT;
return l;
}
static inline bool
JSVAL_IS_NULL_IMPL(jsval_layout l)
{
return l.asBits == JSVAL_SHIFTED_TAG_NULL;
}
static inline bool
JSVAL_IS_GCTHING_IMPL(jsval_layout l)
{
return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET;
}
static inline js::gc::Cell*
JSVAL_TO_GCTHING_IMPL(jsval_layout l)
{
uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK;
MOZ_ASSERT((ptrBits & 0x7) == 0);
return reinterpret_cast<js::gc::Cell*>(ptrBits);
}
static inline uint32_t
JSVAL_TRACE_KIND_IMPL(jsval_layout l)
{
static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
"Value type tags must correspond with JS::TraceKinds.");
return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) & 0x03;
}
static inline jsval_layout
PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
{
jsval_layout l;
uint64_t ptrBits = (uint64_t)ptr;
MOZ_ASSERT((ptrBits & 1) == 0);
l.asBits = ptrBits >> 1;
MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
return l;
}
static inline void*
JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
{
MOZ_ASSERT((l.asBits & 0x8000000000000000LL) == 0);
return (void*)(l.asBits << 1);
}
static inline bool
JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32)
{
return l.asBits == (((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32);
}
static inline bool
JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(jsval_layout l, bool b)
{
return l.asBits == (((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN);
}
static inline jsval_layout
MAGIC_TO_JSVAL_IMPL(JSWhyMagic why)
{
jsval_layout l;
l.asBits = ((uint64_t)(uint32_t)why) | JSVAL_SHIFTED_TAG_MAGIC;
return l;
}
static inline jsval_layout
MAGIC_UINT32_TO_JSVAL_IMPL(uint32_t payload)
{
jsval_layout l;
l.asBits = ((uint64_t)payload) | JSVAL_SHIFTED_TAG_MAGIC;
return l;
}
static inline bool
JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs)
{
uint64_t lbits = lhs.asBits, rbits = rhs.asBits;
return (lbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE && rbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE) ||
(((lbits ^ rbits) & 0xFFFF800000000000LL) == 0);
}
static inline JSValueType
JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l)
{
uint64_t type = (l.asBits >> JSVAL_TAG_SHIFT) & 0xF;
MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
return (JSValueType)type;
}
#endif /* JS_PUNBOX64 */
static inline bool
JSVAL_IS_TRACEABLE_IMPL(jsval_layout l)
{
return JSVAL_IS_GCTHING_IMPL(l) && !JSVAL_IS_NULL_IMPL(l);
}
static inline jsval_layout JSVAL_TO_IMPL(JS::Value v);
static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(jsval_layout l);
namespace JS {
static inline JS_VALUE_CONSTEXPR JS::Value UndefinedValue();
/**
* Returns a generic quiet NaN value, with all payload bits set to zero.
*
* Among other properties, this NaN's bit pattern conforms to JS::Value's
* bit pattern restrictions.
*/
static MOZ_ALWAYS_INLINE double
GenericNaN()
{
return mozilla::SpecificNaN<double>(0, 0x8000000000000ULL);
}
/* MSVC with PGO miscompiles this function. */
#if defined(_MSC_VER)
# pragma optimize("g", off)
#endif
static inline double
CanonicalizeNaN(double d)
{
if (MOZ_UNLIKELY(mozilla::IsNaN(d)))
return GenericNaN();
return d;
}
#if defined(_MSC_VER)
# pragma optimize("", on)
#endif
/**
* JS::Value is the interface for a single JavaScript Engine value. A few
* general notes on JS::Value:
*
* - JS::Value has setX() and isX() members for X in
*
* { Int32, Double, String, Symbol, Boolean, Undefined, Null, Object, Magic }
*
* JS::Value also contains toX() for each of the non-singleton types.
*
* - Magic is a singleton type whose payload contains either a JSWhyMagic "reason" for
* the magic value or a uint32_t value. By providing JSWhyMagic values when
* creating and checking for magic values, it is possible to assert, at
* runtime, that only magic values with the expected reason flow through a
* particular value. For example, if cx->exception has a magic value, the
* reason must be JS_GENERATOR_CLOSING.
*
* - The JS::Value operations are preferred. The JSVAL_* operations remain for
* compatibility; they may be removed at some point. These operations mostly
* provide similar functionality. But there are a few key differences. One
* is that JS::Value gives null a separate type.
* Also, to help prevent mistakenly boxing a nullable JSObject* as an object,
* Value::setObject takes a JSObject&. (Conversely, Value::toObject returns a
* JSObject&.) A convenience member Value::setObjectOrNull is provided.
*
* - JSVAL_VOID is the same as the singleton value of the Undefined type.
*
* - Note that JS::Value is 8 bytes on 32 and 64-bit architectures. Thus, on
* 32-bit user code should avoid copying jsval/JS::Value as much as possible,
* preferring to pass by const Value&.
*/
class Value
{
public:
/*
* N.B. the default constructor leaves Value unitialized. Adding a default
* constructor prevents Value from being stored in a union.
*/
#if defined(JS_VALUE_IS_CONSTEXPR)
Value() = default;
Value(const Value& v) = default;
#endif
/**
* Returns false if creating a NumberValue containing the given type would
* be lossy, true otherwise.
*/
template <typename T>
static bool isNumberRepresentable(const T t) {
return T(double(t)) == t;
}
/*** Mutators ***/
void setNull() {
data.asBits = BUILD_JSVAL(JSVAL_TAG_NULL, 0).asBits;
}
void setUndefined() {
data.asBits = BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0).asBits;
}
void setInt32(int32_t i) {
data = INT32_TO_JSVAL_IMPL(i);
}
int32_t& getInt32Ref() {
MOZ_ASSERT(isInt32());
return data.s.payload.i32;
}
void setDouble(double d) {
data = DOUBLE_TO_JSVAL_IMPL(d);
}
void setNaN() {
setDouble(GenericNaN());
}
double& getDoubleRef() {
MOZ_ASSERT(isDouble());
return data.asDouble;
}
void setString(JSString* str) {
data = STRING_TO_JSVAL_IMPL(str);
}
void setSymbol(JS::Symbol* sym) {
data = SYMBOL_TO_JSVAL_IMPL(sym);
}
void setObject(JSObject& obj) {
data = OBJECT_TO_JSVAL_IMPL(&obj);
}
void setBoolean(bool b) {
data = BOOLEAN_TO_JSVAL_IMPL(b);
}
void setMagic(JSWhyMagic why) {
data = MAGIC_TO_JSVAL_IMPL(why);
}
void setMagicUint32(uint32_t payload) {
data = MAGIC_UINT32_TO_JSVAL_IMPL(payload);
}
bool setNumber(uint32_t ui) {
if (ui > JSVAL_INT_MAX) {
setDouble((double)ui);
return false;
} else {
setInt32((int32_t)ui);
return true;
}
}
bool setNumber(double d) {
int32_t i;
if (mozilla::NumberIsInt32(d, &i)) {
setInt32(i);
return true;
}
setDouble(d);
return false;
}
void setObjectOrNull(JSObject* arg) {
if (arg)
setObject(*arg);
else
setNull();
}
void swap(Value& rhs) {
uint64_t tmp = rhs.data.asBits;
rhs.data.asBits = data.asBits;
data.asBits = tmp;
}
/*** Value type queries ***/
bool isUndefined() const {
return JSVAL_IS_UNDEFINED_IMPL(data);
}
bool isNull() const {
return JSVAL_IS_NULL_IMPL(data);
}
bool isNullOrUndefined() const {
return isNull() || isUndefined();
}
bool isInt32() const {
return JSVAL_IS_INT32_IMPL(data);
}
bool isInt32(int32_t i32) const {
return JSVAL_IS_SPECIFIC_INT32_IMPL(data, i32);
}
bool isDouble() const {
return JSVAL_IS_DOUBLE_IMPL(data);
}
bool isNumber() const {
return JSVAL_IS_NUMBER_IMPL(data);
}
bool isString() const {
return JSVAL_IS_STRING_IMPL(data);
}
bool isSymbol() const {
return JSVAL_IS_SYMBOL_IMPL(data);
}
bool isObject() const {
return JSVAL_IS_OBJECT_IMPL(data);
}
bool isPrimitive() const {
return JSVAL_IS_PRIMITIVE_IMPL(data);
}
bool isObjectOrNull() const {
return JSVAL_IS_OBJECT_OR_NULL_IMPL(data);
}
bool isGCThing() const {
return JSVAL_IS_GCTHING_IMPL(data);
}
bool isBoolean() const {
return JSVAL_IS_BOOLEAN_IMPL(data);
}
bool isTrue() const {
return JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(data, true);
}
bool isFalse() const {
return JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(data, false);
}
bool isMagic() const {
return JSVAL_IS_MAGIC_IMPL(data);
}
bool isMagic(JSWhyMagic why) const {
MOZ_ASSERT_IF(isMagic(), data.s.payload.why == why);
return JSVAL_IS_MAGIC_IMPL(data);
}
bool isMarkable() const {
return JSVAL_IS_TRACEABLE_IMPL(data);
}
JS::TraceKind traceKind() const {
MOZ_ASSERT(isMarkable());
return JS::TraceKind(JSVAL_TRACE_KIND_IMPL(data));
}
JSWhyMagic whyMagic() const {
MOZ_ASSERT(isMagic());
return data.s.payload.why;
}
uint32_t magicUint32() const {
MOZ_ASSERT(isMagic());
return data.s.payload.u32;
}
/*** Comparison ***/
bool operator==(const Value& rhs) const {
return data.asBits == rhs.data.asBits;
}
bool operator!=(const Value& rhs) const {
return data.asBits != rhs.data.asBits;
}
friend inline bool SameType(const Value& lhs, const Value& rhs);
/*** Extract the value's typed payload ***/
int32_t toInt32() const {
MOZ_ASSERT(isInt32());
return JSVAL_TO_INT32_IMPL(data);
}
double toDouble() const {
MOZ_ASSERT(isDouble());
return data.asDouble;
}
double toNumber() const {
MOZ_ASSERT(isNumber());
return isDouble() ? toDouble() : double(toInt32());
}
JSString* toString() const {
MOZ_ASSERT(isString());
return JSVAL_TO_STRING_IMPL(data);
}
JS::Symbol* toSymbol() const {
MOZ_ASSERT(isSymbol());
return JSVAL_TO_SYMBOL_IMPL(data);
}
JSObject& toObject() const {
MOZ_ASSERT(isObject());
return *JSVAL_TO_OBJECT_IMPL(data);
}
JSObject* toObjectOrNull() const {
MOZ_ASSERT(isObjectOrNull());
return JSVAL_TO_OBJECT_IMPL(data);
}
js::gc::Cell* toGCThing() const {
MOZ_ASSERT(isGCThing());
return JSVAL_TO_GCTHING_IMPL(data);
}
GCCellPtr toGCCellPtr() const {
return GCCellPtr(toGCThing(), traceKind());
}
bool toBoolean() const {
MOZ_ASSERT(isBoolean());
return JSVAL_TO_BOOLEAN_IMPL(data);
}
uint32_t payloadAsRawUint32() const {
MOZ_ASSERT(!isDouble());
return data.s.payload.u32;
}
uint64_t asRawBits() const {
return data.asBits;
}
JSValueType extractNonDoubleType() const {
return JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(data);
}
/*
* Private API
*
* Private setters/getters allow the caller to read/write arbitrary types
* that fit in the 64-bit payload. It is the caller's responsibility, after
* storing to a value with setPrivateX to read only using getPrivateX.
* Privates values are given a type which ensures they are not marked.
*/
void setPrivate(void* ptr) {
data = PRIVATE_PTR_TO_JSVAL_IMPL(ptr);
}
void* toPrivate() const {
MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(data));
return JSVAL_TO_PRIVATE_PTR_IMPL(data);
}
void setPrivateUint32(uint32_t ui) {
MOZ_ASSERT(uint32_t(int32_t(ui)) == ui);
setInt32(int32_t(ui));
}
uint32_t toPrivateUint32() const {
return uint32_t(toInt32());
}
/*
* An unmarked value is just a void* cast as a Value. Thus, the Value is
* not safe for GC and must not be marked. This API avoids raw casts
* and the ensuing strict-aliasing warnings.
*/
void setUnmarkedPtr(void* ptr) {
data.asPtr = ptr;
}
void* toUnmarkedPtr() const {
return data.asPtr;
}
const size_t* payloadWord() const {
#if defined(JS_NUNBOX32)
return &data.s.payload.word;
#elif defined(JS_PUNBOX64)
return &data.asWord;
#endif
}
const uintptr_t* payloadUIntPtr() const {
#if defined(JS_NUNBOX32)
return &data.s.payload.uintptr;
#elif defined(JS_PUNBOX64)
return &data.asUIntPtr;
#endif
}
#if !defined(_MSC_VER) && !defined(__sparc)
// Value must be POD so that MSVC will pass it by value and not in memory
// (bug 689101); the same is true for SPARC as well (bug 737344). More
// precisely, we don't want Value return values compiled as out params.
private:
#endif
jsval_layout data;
private:
#if defined(JS_VALUE_IS_CONSTEXPR)
MOZ_IMPLICIT JS_VALUE_CONSTEXPR Value(jsval_layout layout) : data(layout) {}
#endif
void staticAssertions() {
JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
JS_STATIC_ASSERT(sizeof(JSValueTag) == 4);
JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4);
JS_STATIC_ASSERT(sizeof(Value) == 8);
}
friend jsval_layout (::JSVAL_TO_IMPL)(Value);
friend Value JS_VALUE_CONSTEXPR (::IMPL_TO_JSVAL)(jsval_layout l);
friend Value JS_VALUE_CONSTEXPR (JS::UndefinedValue)();
};
inline bool
IsOptimizedPlaceholderMagicValue(const Value& v)
{
if (v.isMagic()) {
MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
return true;
}
return false;
}
static MOZ_ALWAYS_INLINE void
ExposeValueToActiveJS(const Value& v)
{
if (v.isMarkable())
js::gc::ExposeGCThingToActiveJS(GCCellPtr(v));
}
/************************************************************************/
static inline Value
NullValue()
{
Value v;
v.setNull();
return v;
}
static inline JS_VALUE_CONSTEXPR Value
UndefinedValue()
{
#if defined(JS_VALUE_IS_CONSTEXPR)
return Value(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0));
#else
JS::Value v;
v.setUndefined();
return v;
#endif
}
static inline JS_VALUE_CONSTEXPR Value
Int32Value(int32_t i32)
{
return IMPL_TO_JSVAL(INT32_TO_JSVAL_IMPL(i32));
}
static inline Value
DoubleValue(double dbl)
{
Value v;
v.setDouble(dbl);
return v;
}
static inline JS_VALUE_CONSTEXPR Value
CanonicalizedDoubleValue(double d)
{
/*
* This is a manually inlined version of:
* d = JS_CANONICALIZE_NAN(d);
* return IMPL_TO_JSVAL(DOUBLE_TO_JSVAL_IMPL(d));
* because GCC from XCode 3.1.4 miscompiles the above code.
*/
#if defined(JS_VALUE_IS_CONSTEXPR)
return IMPL_TO_JSVAL(MOZ_UNLIKELY(mozilla::IsNaN(d))
? (jsval_layout) { .asBits = 0x7FF8000000000000LL }
: (jsval_layout) { .asDouble = d });
#else
jsval_layout l;
if (MOZ_UNLIKELY(d != d))
l.asBits = 0x7FF8000000000000LL;
else
l.asDouble = d;
return IMPL_TO_JSVAL(l);
#endif
}
static inline Value
DoubleNaNValue()
{
Value v;
v.setNaN();
return v;
}
static inline Value
Float32Value(float f)
{
Value v;
v.setDouble(f);
return v;
}
static inline Value
StringValue(JSString* str)
{
Value v;
v.setString(str);
return v;
}
static inline Value
SymbolValue(JS::Symbol* sym)
{
Value v;
v.setSymbol(sym);
return v;
}
static inline Value
BooleanValue(bool boo)
{
Value v;
v.setBoolean(boo);
return v;
}
static inline Value
TrueValue()
{
Value v;
v.setBoolean(true);
return v;
}
static inline Value
FalseValue()
{
Value v;
v.setBoolean(false);
return v;
}
static inline Value
ObjectValue(JSObject& obj)
{
Value v;
v.setObject(obj);
return v;
}
static inline Value
ObjectValueCrashOnTouch()
{
Value v;
v.setObject(*reinterpret_cast<JSObject*>(0x42));
return v;
}
static inline Value
MagicValue(JSWhyMagic why)
{
Value v;
v.setMagic(why);
return v;
}
static inline Value
MagicValueUint32(uint32_t payload)
{
Value v;
v.setMagicUint32(payload);
return v;
}
static inline Value
NumberValue(float f)
{
Value v;
v.setNumber(f);
return v;
}
static inline Value
NumberValue(double dbl)
{
Value v;
v.setNumber(dbl);
return v;
}
static inline Value
NumberValue(int8_t i)
{
return Int32Value(i);
}
static inline Value
NumberValue(uint8_t i)
{
return Int32Value(i);
}
static inline Value
NumberValue(int16_t i)
{
return Int32Value(i);
}
static inline Value
NumberValue(uint16_t i)
{
return Int32Value(i);
}
static inline Value
NumberValue(int32_t i)
{
return Int32Value(i);
}
static inline JS_VALUE_CONSTEXPR Value
NumberValue(uint32_t i)
{
return i <= JSVAL_INT_MAX
? Int32Value(int32_t(i))
: CanonicalizedDoubleValue(double(i));
}
namespace detail {
template <bool Signed>
class MakeNumberValue
{
public:
template<typename T>
static inline Value create(const T t)
{
Value v;
if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX)
v.setInt32(int32_t(t));
else
v.setDouble(double(t));
return v;
}
};
template <>
class MakeNumberValue<false>
{
public:
template<typename T>
static inline Value create(const T t)
{
Value v;
if (t <= JSVAL_INT_MAX)
v.setInt32(int32_t(t));
else
v.setDouble(double(t));
return v;
}
};
} // namespace detail
template <typename T>
static inline Value
NumberValue(const T t)
{
MOZ_ASSERT(Value::isNumberRepresentable(t), "value creation would be lossy");
return detail::MakeNumberValue<std::numeric_limits<T>::is_signed>::create(t);
}
static inline Value
ObjectOrNullValue(JSObject* obj)
{
Value v;
v.setObjectOrNull(obj);
return v;
}
static inline Value
PrivateValue(void* ptr)
{
Value v;
v.setPrivate(ptr);
return v;
}
static inline Value
PrivateUint32Value(uint32_t ui)
{
Value v;
v.setPrivateUint32(ui);
return v;
}
inline bool
SameType(const Value& lhs, const Value& rhs)
{
return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data);
}
} // namespace JS
/************************************************************************/
namespace JS {
JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep, const Value& prev, const Value& next);
} // namespace JS
namespace js {
template <> struct GCMethods<const JS::Value>
{
static JS::Value initial() { return JS::UndefinedValue(); }
};
template <> struct GCMethods<JS::Value>
{
static JS::Value initial() { return JS::UndefinedValue(); }
static gc::Cell* asGCThingOrNull(const JS::Value& v) {
return v.isMarkable() ? v.toGCThing() : nullptr;
}
static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
JS::HeapValuePostBarrier(v, prev, next);
}
};
template <class Outer> class MutableValueOperations;
/**
* A class designed for CRTP use in implementing the non-mutating parts of the
* Value interface in Value-like classes. Outer must be a class inheriting
* ValueOperations<Outer> with a visible get() method returning a const
* reference to the Value abstracted by Outer.
*/
template <class Outer>
class ValueOperations
{
friend class MutableValueOperations<Outer>;
const JS::Value& value() const { return static_cast<const Outer*>(this)->get(); }
public:
bool isUndefined() const { return value().isUndefined(); }
bool isNull() const { return value().isNull(); }
bool isBoolean() const { return value().isBoolean(); }
bool isTrue() const { return value().isTrue(); }
bool isFalse() const { return value().isFalse(); }
bool isNumber() const { return value().isNumber(); }
bool isInt32() const { return value().isInt32(); }
bool isInt32(int32_t i32) const { return value().isInt32(i32); }
bool isDouble() const { return value().isDouble(); }
bool isString() const { return value().isString(); }
bool isSymbol() const { return value().isSymbol(); }
bool isObject() const { return value().isObject(); }
bool isMagic() const { return value().isMagic(); }
bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
bool isMarkable() const { return value().isMarkable(); }
bool isPrimitive() const { return value().isPrimitive(); }
bool isGCThing() const { return value().isGCThing(); }
bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
bool isObjectOrNull() const { return value().isObjectOrNull(); }
bool toBoolean() const { return value().toBoolean(); }
double toNumber() const { return value().toNumber(); }
int32_t toInt32() const { return value().toInt32(); }
double toDouble() const { return value().toDouble(); }
JSString* toString() const { return value().toString(); }
JS::Symbol* toSymbol() const { return value().toSymbol(); }
JSObject& toObject() const { return value().toObject(); }
JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
gc::Cell* toGCThing() const { return value().toGCThing(); }
JS::TraceKind traceKind() const { return value().traceKind(); }
uint64_t asRawBits() const { return value().asRawBits(); }
JSValueType extractNonDoubleType() const { return value().extractNonDoubleType(); }
uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
JSWhyMagic whyMagic() const { return value().whyMagic(); }
uint32_t magicUint32() const { return value().magicUint32(); }
};
/**
* A class designed for CRTP use in implementing all the mutating parts of the
* Value interface in Value-like classes. Outer must be a class inheriting
* MutableValueOperations<Outer> with visible get() methods returning const and
* non-const references to the Value abstracted by Outer.
*/
template <class Outer>
class MutableValueOperations : public ValueOperations<Outer>
{
JS::Value& value() { return static_cast<Outer*>(this)->get(); }
public:
void setNull() { value().setNull(); }
void setUndefined() { value().setUndefined(); }
void setInt32(int32_t i) { value().setInt32(i); }
void setDouble(double d) { value().setDouble(d); }
void setNaN() { setDouble(JS::GenericNaN()); }
void setBoolean(bool b) { value().setBoolean(b); }
void setMagic(JSWhyMagic why) { value().setMagic(why); }
bool setNumber(uint32_t ui) { return value().setNumber(ui); }
bool setNumber(double d) { return value().setNumber(d); }
void setString(JSString* str) { this->value().setString(str); }
void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); }
void setObject(JSObject& obj) { this->value().setObject(obj); }
void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); }
};
/*
* Augment the generic Heap<T> interface when T = Value with
* type-querying, value-extracting, and mutating operations.
*/
template <>
class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> >
{
typedef JS::Heap<JS::Value> Outer;
friend class ValueOperations<Outer>;
void setBarriered(const JS::Value& v) {
*static_cast<JS::Heap<JS::Value>*>(this) = v;
}
public:
void setNull() { setBarriered(JS::NullValue()); }
void setUndefined() { setBarriered(JS::UndefinedValue()); }
void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); }
void setDouble(double d) { setBarriered(JS::DoubleValue(d)); }
void setNaN() { setDouble(JS::GenericNaN()); }
void setBoolean(bool b) { setBarriered(JS::BooleanValue(b)); }
void setMagic(JSWhyMagic why) { setBarriered(JS::MagicValue(why)); }
void setString(JSString* str) { setBarriered(JS::StringValue(str)); }
void setSymbol(JS::Symbol* sym) { setBarriered(JS::SymbolValue(sym)); }
void setObject(JSObject& obj) { setBarriered(JS::ObjectValue(obj)); }
bool setNumber(uint32_t ui) {
if (ui > JSVAL_INT_MAX) {
setDouble((double)ui);
return false;
} else {
setInt32((int32_t)ui);
return true;
}
}
bool setNumber(double d) {
int32_t i;
if (mozilla::NumberIsInt32(d, &i)) {
setInt32(i);
return true;
}
setDouble(d);
return false;
}
void setObjectOrNull(JSObject* arg) {
if (arg)
setObject(*arg);
else
setNull();
}
};
template <>
class HandleBase<JS::Value> : public ValueOperations<JS::Handle<JS::Value> >
{};
template <>
class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> >
{};
template <>
class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> >
{};
template <>
class PersistentRootedBase<JS::Value> : public MutableValueOperations<JS::PersistentRooted<JS::Value>>
{};
/*
* If the Value is a GC pointer type, convert to that type and call |f| with
* the pointer. If the Value is not a GC type, calls F::defaultValue.
*/
template <typename F, typename... Args>
auto
DispatchTyped(F f, const JS::Value& val, Args&&... args)
-> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
{
if (val.isString())
return f(val.toString(), mozilla::Forward<Args>(args)...);
if (val.isObject())
return f(&val.toObject(), mozilla::Forward<Args>(args)...);
if (val.isSymbol())
return f(val.toSymbol(), mozilla::Forward<Args>(args)...);
MOZ_ASSERT(!val.isMarkable());
return F::defaultValue(val);
}
template <class S> struct VoidDefaultAdaptor { static void defaultValue(S) {} };
template <class S> struct IdentityDefaultAdaptor { static S defaultValue(const S& v) {return v;} };
template <class S, bool v> struct BoolDefaultAdaptor { static bool defaultValue(S) { return v; } };
} // namespace js
inline jsval_layout
JSVAL_TO_IMPL(JS::Value v)
{
return v.data;
}
inline JS_VALUE_CONSTEXPR JS::Value
IMPL_TO_JSVAL(jsval_layout l)
{
#if defined(JS_VALUE_IS_CONSTEXPR)
return JS::Value(l);
#else
JS::Value v;
v.data = l;
return v;
#endif
}
namespace JS {
#ifdef JS_DEBUG
namespace detail {
struct ValueAlignmentTester { char c; JS::Value v; };
static_assert(sizeof(ValueAlignmentTester) == 16,
"JS::Value must be 16-byte-aligned");
struct LayoutAlignmentTester { char c; jsval_layout l; };
static_assert(sizeof(LayoutAlignmentTester) == 16,
"jsval_layout must be 16-byte-aligned");
} // namespace detail
#endif /* JS_DEBUG */
} // namespace JS
static_assert(sizeof(jsval_layout) == sizeof(JS::Value),
"jsval_layout and JS::Value must have identical layouts");
/************************************************************************/
namespace JS {
extern JS_PUBLIC_DATA(const HandleValue) NullHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) TrueHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) FalseHandleValue;
} // namespace JS
#undef JS_VALUE_IS_CONSTEXPR
#undef JS_RETURN_LAYOUT_FROM_BITS
#endif /* js_Value_h */