3dgfx add cmake & remove spidermonkey glfw (#82)
* add sdl * add SDL.h * update libs * update source code * add sdl * update libs * update source code * compile ios * remove spidermonkey & glfw
This commit is contained in:
parent
b0524ddeef
commit
7d8223bccb
|
|
@ -51,6 +51,14 @@ set_target_properties(z PROPERTIES
|
|||
INTERFACE_INCLUDE_DIRECTORIES ${platform_spec_path}/include/zlib
|
||||
)
|
||||
|
||||
add_library(android_platform
|
||||
${CMAKE_ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
|
||||
${CMAKE_ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c
|
||||
)
|
||||
target_include_directories(android_platform PUBLIC
|
||||
${CMAKE_ANDROID_NDK}/sources/android/cpufeatures
|
||||
${CMAKE_ANDROID_NDK}/sources/android/native_app_glue
|
||||
)
|
||||
|
||||
set(se_libs_name)
|
||||
|
||||
|
|
@ -165,18 +173,22 @@ if(USE_SE_V8 AND USE_V8_DEBUGGER)
|
|||
)
|
||||
endif()
|
||||
|
||||
|
||||
list(APPEND CC_EXTERNAL_LIBS
|
||||
freetype
|
||||
jpeg
|
||||
png
|
||||
uv
|
||||
webp
|
||||
OpenSLES
|
||||
${se_libs_name}
|
||||
z
|
||||
android_platform
|
||||
)
|
||||
|
||||
|
||||
list(APPEND CC_EXTERNAL_INCLUDES
|
||||
${platform_spec_path}/include
|
||||
${platform_spec_path}/include/v8
|
||||
${platform_spec_path}/include/uv
|
||||
${CMAKE_ANDROID_NDK}/sources/android/native_app_glue
|
||||
)
|
||||
|
|
@ -1,22 +1,5 @@
|
|||
|
||||
# set friendly platform define
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(WINDOWS TRUE)
|
||||
set(SYSTEM_STRING "Windows Desktop")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Android")
|
||||
set(ANDROID TRUE)
|
||||
set(SYSTEM_STRING "Android")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
if(IOS)
|
||||
set(APPLE TRUE)
|
||||
set(SYSTEM_STRING "IOS")
|
||||
else()
|
||||
set(APPLE TRUE)
|
||||
set(MACOSX TRUE)
|
||||
set(SYSTEM_STRING "Mac OSX")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
set(platform_name ios)
|
||||
set(platform_spec_path ios)
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* from: @(#)fdlibm.h 5.1 93/09/24
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef mozilla_imported_fdlibm_h
|
||||
#define mozilla_imported_fdlibm_h
|
||||
|
||||
namespace fdlibm {
|
||||
|
||||
double acos(double);
|
||||
double asin(double);
|
||||
double atan(double);
|
||||
double atan2(double, double);
|
||||
|
||||
double cosh(double);
|
||||
double sinh(double);
|
||||
double tanh(double);
|
||||
|
||||
double exp(double);
|
||||
double log(double);
|
||||
double log10(double);
|
||||
|
||||
double pow(double, double);
|
||||
double sqrt(double);
|
||||
double fabs(double);
|
||||
|
||||
double floor(double);
|
||||
double trunc(double);
|
||||
double ceil(double);
|
||||
|
||||
double acosh(double);
|
||||
double asinh(double);
|
||||
double atanh(double);
|
||||
double cbrt(double);
|
||||
double expm1(double);
|
||||
double hypot(double, double);
|
||||
double log1p(double);
|
||||
double log2(double);
|
||||
double rint(double);
|
||||
double copysign(double, double);
|
||||
double nearbyint(double);
|
||||
double scalbn(double, int);
|
||||
|
||||
float ceilf(float);
|
||||
float floorf(float);
|
||||
|
||||
float nearbyintf(float);
|
||||
float rintf(float);
|
||||
float truncf(float);
|
||||
|
||||
} /* namespace fdlibm */
|
||||
|
||||
#endif /* mozilla_imported_fdlibm_h */
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 8; c-basic-offset: 8 -*- */
|
||||
/* vim:set softtabstop=8 shiftwidth=8: */
|
||||
/*-
|
||||
* Copyright (C) 2006-2008 Jason Evans <jasone@FreeBSD.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer as
|
||||
* the first lines of this file unmodified other than the possible
|
||||
* addition of one or more copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _JEMALLOC_TYPES_H_
|
||||
#define _JEMALLOC_TYPES_H_
|
||||
|
||||
/* grab size_t */
|
||||
#ifdef _MSC_VER
|
||||
#include <crtdefs.h>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef unsigned char jemalloc_bool;
|
||||
|
||||
/*
|
||||
* jemalloc_stats() is not a stable interface. When using jemalloc_stats_t, be
|
||||
* sure that the compiled results of jemalloc.c are in sync with this header
|
||||
* file.
|
||||
*/
|
||||
typedef struct {
|
||||
/*
|
||||
* Run-time configuration settings.
|
||||
*/
|
||||
jemalloc_bool opt_abort; /* abort(3) on error? */
|
||||
jemalloc_bool opt_junk; /* Fill allocated memory with 0xe4? */
|
||||
jemalloc_bool opt_poison; /* Fill free memory with 0xe5? */
|
||||
jemalloc_bool opt_utrace; /* Trace all allocation events? */
|
||||
jemalloc_bool opt_sysv; /* SysV semantics? */
|
||||
jemalloc_bool opt_xmalloc; /* abort(3) on OOM? */
|
||||
jemalloc_bool opt_zero; /* Fill allocated memory with 0x0? */
|
||||
size_t narenas; /* Number of arenas. */
|
||||
size_t balance_threshold; /* Arena contention rebalance threshold. */
|
||||
size_t quantum; /* Allocation quantum. */
|
||||
size_t small_max; /* Max quantum-spaced allocation size. */
|
||||
size_t large_max; /* Max sub-chunksize allocation size. */
|
||||
size_t chunksize; /* Size of each virtual memory mapping. */
|
||||
size_t dirty_max; /* Max dirty pages per arena. */
|
||||
|
||||
/*
|
||||
* Current memory usage statistics.
|
||||
*/
|
||||
size_t mapped; /* Bytes mapped (not necessarily committed). */
|
||||
size_t allocated; /* Bytes allocated (committed, in use by application). */
|
||||
size_t waste; /* Bytes committed, not in use by the
|
||||
application, and not intentionally left
|
||||
unused (i.e., not dirty). */
|
||||
size_t page_cache; /* Committed, unused pages kept around as a
|
||||
cache. (jemalloc calls these "dirty".) */
|
||||
size_t bookkeeping; /* Committed bytes used internally by the
|
||||
allocator. */
|
||||
size_t bin_unused; /* Bytes committed to a bin but currently unused. */
|
||||
} jemalloc_stats_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* _JEMALLOC_TYPES_H_ */
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=78:
|
||||
*
|
||||
* 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 js_config_h
|
||||
#define js_config_h
|
||||
|
||||
/* Definitions set at build time that affect SpiderMonkey's public API.
|
||||
This header file is generated by the SpiderMonkey configure script,
|
||||
and installed along with jsapi.h. */
|
||||
|
||||
/* Define to 1 if SpiderMonkey is in debug mode. */
|
||||
/* #undef JS_DEBUG */
|
||||
|
||||
/*
|
||||
* NB: We have a special case for rust-bindgen, which wants to be able to
|
||||
* generate both debug and release bindings on a single objdir.
|
||||
*/
|
||||
#ifdef JS_DEBUG
|
||||
#if !defined(DEBUG) && !defined(RUST_BINDGEN)
|
||||
# error "SpiderMonkey was configured with --enable-debug, so DEBUG must be defined when including this header"
|
||||
# endif
|
||||
#else
|
||||
# if defined(DEBUG) && !defined(RUST_BINDGEN)
|
||||
# error "SpiderMonkey was configured with --disable-debug, so DEBUG must be not defined when including this header"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Define to 1 if SpiderMonkey should not use struct types in debug builds. */
|
||||
/* #undef JS_NO_JSVAL_JSID_STRUCT_TYPES */
|
||||
|
||||
/* Define to 1 if SpiderMonkey should support multi-threaded clients. */
|
||||
/* #undef JS_THREADSAFE */
|
||||
|
||||
/* Define to 1 if SpiderMonkey should include ctypes support. */
|
||||
/* #undef JS_HAS_CTYPES */
|
||||
|
||||
/* Define to 1 if SpiderMonkey should support the ability to perform
|
||||
entirely too much GC. */
|
||||
/* #undef JS_GC_ZEAL */
|
||||
|
||||
/* Define to 1 if SpiderMonkey should use small chunks. */
|
||||
/* #undef JS_GC_SMALL_CHUNK_SIZE */
|
||||
|
||||
/* Define to 1 to perform extra assertions and heap poisoning. */
|
||||
/* #undef JS_CRASH_DIAGNOSTICS */
|
||||
|
||||
/* Define to 1 if SpiderMonkey is in NUNBOX32 mode. */
|
||||
#define JS_NUNBOX32 1
|
||||
|
||||
/* Define to 1 if SpiderMonkey is in PUNBOX64 mode. */
|
||||
/* #undef JS_PUNBOX64 */
|
||||
|
||||
/* MOZILLA JSAPI version number components */
|
||||
#define MOZJS_MAJOR_VERSION 52
|
||||
#define MOZJS_MINOR_VERSION 0
|
||||
|
||||
#endif /* js_config_h */
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=78:
|
||||
*
|
||||
* 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 js_config_h
|
||||
#define js_config_h
|
||||
|
||||
/* Definitions set at build time that affect SpiderMonkey's public API.
|
||||
This header file is generated by the SpiderMonkey configure script,
|
||||
and installed along with jsapi.h. */
|
||||
|
||||
/* Define to 1 if SpiderMonkey is in debug mode. */
|
||||
/* #undef JS_DEBUG */
|
||||
|
||||
/*
|
||||
* NB: We have a special case for rust-bindgen, which wants to be able to
|
||||
* generate both debug and release bindings on a single objdir.
|
||||
*/
|
||||
#ifdef JS_DEBUG
|
||||
#if !defined(DEBUG) && !defined(RUST_BINDGEN)
|
||||
# error "SpiderMonkey was configured with --enable-debug, so DEBUG must be defined when including this header"
|
||||
# endif
|
||||
#else
|
||||
# if defined(DEBUG) && !defined(RUST_BINDGEN)
|
||||
# error "SpiderMonkey was configured with --disable-debug, so DEBUG must be not defined when including this header"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Define to 1 if SpiderMonkey should not use struct types in debug builds. */
|
||||
/* #undef JS_NO_JSVAL_JSID_STRUCT_TYPES */
|
||||
|
||||
/* Define to 1 if SpiderMonkey should support multi-threaded clients. */
|
||||
/* #undef JS_THREADSAFE */
|
||||
|
||||
/* Define to 1 if SpiderMonkey should include ctypes support. */
|
||||
/* #undef JS_HAS_CTYPES */
|
||||
|
||||
/* Define to 1 if SpiderMonkey should support the ability to perform
|
||||
entirely too much GC. */
|
||||
/* #undef JS_GC_ZEAL */
|
||||
|
||||
/* Define to 1 if SpiderMonkey should use small chunks. */
|
||||
/* #undef JS_GC_SMALL_CHUNK_SIZE */
|
||||
|
||||
/* Define to 1 to perform extra assertions and heap poisoning. */
|
||||
/* #undef JS_CRASH_DIAGNOSTICS */
|
||||
|
||||
/* Define to 1 if SpiderMonkey is in NUNBOX32 mode. */
|
||||
/* #undef JS_NUNBOX32 */
|
||||
|
||||
/* Define to 1 if SpiderMonkey is in PUNBOX64 mode. */
|
||||
#define JS_PUNBOX64 1
|
||||
|
||||
/* MOZILLA JSAPI version number components */
|
||||
#define MOZJS_MAJOR_VERSION 52
|
||||
#define MOZJS_MINOR_VERSION 0
|
||||
|
||||
#endif /* js_config_h */
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#if defined(__LP64__) && __LP64__
|
||||
#include"js-config-64.h"
|
||||
#else
|
||||
#include"js-config-32.h"
|
||||
#endif
|
||||
|
|
@ -1,581 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; 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/. */
|
||||
|
||||
/*
|
||||
* This is the JavaScript error message file.
|
||||
*
|
||||
* The format for each JS error message is:
|
||||
*
|
||||
* MSG_DEF(<SYMBOLIC_NAME>, <ARGUMENT_COUNT>, <EXCEPTION_NAME>,
|
||||
* <FORMAT_STRING>)
|
||||
*
|
||||
* where ;
|
||||
* <SYMBOLIC_NAME> is a legal C identifer that will be used in the
|
||||
* JS engine source.
|
||||
*
|
||||
* <ARGUMENT_COUNT> is an integer literal specifying the total number of
|
||||
* replaceable arguments in the following format string.
|
||||
*
|
||||
* <EXCEPTION_NAME> is an exception index from the enum in jsexn.c;
|
||||
* JSEXN_NONE for none. The given exception index will be raised by the
|
||||
* engine when the corresponding error occurs.
|
||||
*
|
||||
* <FORMAT_STRING> is a string literal, optionally containing sequences
|
||||
* {X} where X is an integer representing the argument number that will
|
||||
* be replaced with a string value when the error is reported.
|
||||
*
|
||||
* e.g.
|
||||
*
|
||||
* MSG_DEF(JSMSG_NOT_A_SUBSPECIES, 2, JSEXN_NONE,
|
||||
* "{0} is not a member of the {1} family")
|
||||
*
|
||||
* can be used:
|
||||
*
|
||||
* JS_ReportErrorNumberASCII(JSMSG_NOT_A_SUBSPECIES, "Rhino", "Monkey");
|
||||
*
|
||||
* to report:
|
||||
*
|
||||
* "Rhino is not a member of the Monkey family"
|
||||
*/
|
||||
|
||||
MSG_DEF(JSMSG_NOT_AN_ERROR, 0, JSEXN_ERR, "<Error #0 is reserved>")
|
||||
MSG_DEF(JSMSG_NOT_DEFINED, 1, JSEXN_REFERENCEERR, "{0} is not defined")
|
||||
MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}")
|
||||
MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}")
|
||||
MSG_DEF(JSMSG_NO_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} has no constructor")
|
||||
MSG_DEF(JSMSG_BAD_SORT_ARG, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")
|
||||
MSG_DEF(JSMSG_CANT_WATCH, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}")
|
||||
MSG_DEF(JSMSG_READ_ONLY, 1, JSEXN_TYPEERR, "{0} is read-only")
|
||||
MSG_DEF(JSMSG_CANT_DELETE, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
|
||||
MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY, 0, JSEXN_TYPEERR, "can't delete non-configurable array element")
|
||||
MSG_DEF(JSMSG_NOT_FUNCTION, 1, JSEXN_TYPEERR, "{0} is not a function")
|
||||
MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} is not a constructor")
|
||||
MSG_DEF(JSMSG_CANT_CONVERT_TO, 2, JSEXN_TYPEERR, "can't convert {0} to {1}")
|
||||
MSG_DEF(JSMSG_TOPRIMITIVE_NOT_CALLABLE, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] property is not a function")
|
||||
MSG_DEF(JSMSG_TOPRIMITIVE_RETURNED_OBJECT, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] method returned an object")
|
||||
MSG_DEF(JSMSG_NO_PROPERTIES, 1, JSEXN_TYPEERR, "{0} has no properties")
|
||||
MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}")
|
||||
MSG_DEF(JSMSG_ARG_INDEX_OUT_OF_RANGE, 1, JSEXN_RANGEERR, "argument {0} accesses an index that is out of range")
|
||||
MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 0, JSEXN_RANGEERR, "array too large due to spread operand(s)")
|
||||
MSG_DEF(JSMSG_BAD_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
|
||||
MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 1, JSEXN_TYPEERR, "invalid {0} usage")
|
||||
MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 0, JSEXN_RANGEERR, "invalid array length")
|
||||
MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}")
|
||||
MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
|
||||
MSG_DEF(JSMSG_GETTER_ONLY, 0, JSEXN_TYPEERR, "setting a property that has only a getter")
|
||||
MSG_DEF(JSMSG_OVERWRITING_ACCESSOR, 1, JSEXN_TYPEERR, "can't overwrite accessor property {0}")
|
||||
MSG_DEF(JSMSG_UNDEFINED_PROP, 1, JSEXN_REFERENCEERR, "reference to undefined property {0}")
|
||||
MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects")
|
||||
MSG_DEF(JSMSG_NESTING_GENERATOR, 0, JSEXN_TYPEERR, "already executing generator")
|
||||
MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
|
||||
MSG_DEF(JSMSG_OBJECT_WATCH_DEPRECATED, 0, JSEXN_WARN, "Object.prototype.watch and unwatch are very slow, non-standard, and deprecated; use a getter/setter instead")
|
||||
MSG_DEF(JSMSG_ARRAYBUFFER_SLICE_DEPRECATED, 0, JSEXN_WARN, "ArrayBuffer.slice is deprecated; use ArrayBuffer.prototype.slice instead")
|
||||
MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 1, JSEXN_TYPEERR, "bad surrogate character {0}")
|
||||
MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
|
||||
MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
|
||||
MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW, 1, JSEXN_TYPEERR, "calling a builtin {0} constructor without new is forbidden")
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
|
||||
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
|
||||
MSG_DEF(JSMSG_UNEXPECTED_TYPE, 2, JSEXN_TYPEERR, "{0} is {1}")
|
||||
MSG_DEF(JSMSG_MISSING_FUN_ARG, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
|
||||
MSG_DEF(JSMSG_NOT_NONNULL_OBJECT, 1, JSEXN_TYPEERR, "{0} is not a non-null object")
|
||||
MSG_DEF(JSMSG_SET_NON_OBJECT_RECEIVER, 1, JSEXN_TYPEERR, "can't assign to properties of {0}: not an object")
|
||||
MSG_DEF(JSMSG_INVALID_DESCRIPTOR, 0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified")
|
||||
MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 1, JSEXN_TYPEERR, "{0}: Object is not extensible")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE, 2, JSEXN_TYPEERR, "can't define property {1}: {0} is not extensible")
|
||||
MSG_DEF(JSMSG_CANT_REDEFINE_PROP, 1, JSEXN_TYPEERR, "can't redefine non-configurable property {0}")
|
||||
MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't redefine array length")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't define array index property past the end of an array with non-writable length")
|
||||
MSG_DEF(JSMSG_BAD_GET_SET_FIELD, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
|
||||
MSG_DEF(JSMSG_THROW_TYPE_ERROR, 0, JSEXN_TYPEERR, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
|
||||
MSG_DEF(JSMSG_NOT_EXPECTED_TYPE, 3, JSEXN_TYPEERR, "{0}: expected {1}, got {2}")
|
||||
MSG_DEF(JSMSG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable")
|
||||
MSG_DEF(JSMSG_NOT_ITERATOR, 1, JSEXN_TYPEERR, "{0} is not iterator")
|
||||
MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 2, JSEXN_WARN, "{0} is being assigned a {1}, but already has one")
|
||||
MSG_DEF(JSMSG_GET_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.iterator]() returned a non-object value")
|
||||
MSG_DEF(JSMSG_NEXT_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "iterator.next() returned a non-object value")
|
||||
MSG_DEF(JSMSG_CANT_SET_PROTO, 0, JSEXN_TYPEERR, "can't set prototype of this object")
|
||||
MSG_DEF(JSMSG_CANT_SET_PROTO_OF, 1, JSEXN_TYPEERR, "can't set prototype of {0}")
|
||||
MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
|
||||
MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
|
||||
MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
|
||||
MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
|
||||
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_THIS, 1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_THIS_ARROW, 0, JSEXN_REFERENCEERR, "|this| used uninitialized in arrow function in class constructor")
|
||||
MSG_DEF(JSMSG_BAD_DERIVED_RETURN, 1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
|
||||
|
||||
// JSON
|
||||
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
|
||||
MSG_DEF(JSMSG_JSON_CYCLIC_VALUE, 0, JSEXN_TYPEERR, "cyclic object value")
|
||||
|
||||
// Runtime errors
|
||||
MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}")
|
||||
MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS, 0, JSEXN_REFERENCEERR, "invalid assignment left-hand side")
|
||||
MSG_DEF(JSMSG_BAD_PROTOTYPE, 1, JSEXN_TYPEERR, "'prototype' property of {0} is not an object")
|
||||
MSG_DEF(JSMSG_IN_NOT_OBJECT, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}")
|
||||
MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 0, JSEXN_RANGEERR, "too many constructor arguments")
|
||||
MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 0, JSEXN_RANGEERR, "too many function arguments")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_LEXICAL, 1, JSEXN_REFERENCEERR, "can't access lexical declaration `{0}' before initialization")
|
||||
MSG_DEF(JSMSG_BAD_CONST_ASSIGN, 1, JSEXN_TYPEERR, "invalid assignment to const `{0}'")
|
||||
MSG_DEF(JSMSG_CANT_DECLARE_GLOBAL_BINDING, 2, JSEXN_TYPEERR, "cannot declare global binding `{0}': {1}")
|
||||
|
||||
// Date
|
||||
MSG_DEF(JSMSG_INVALID_DATE, 0, JSEXN_RANGEERR, "invalid date")
|
||||
MSG_DEF(JSMSG_BAD_TOISOSTRING_PROP, 0, JSEXN_TYPEERR, "toISOString property is not callable")
|
||||
|
||||
// String
|
||||
MSG_DEF(JSMSG_BAD_URI, 0, JSEXN_URIERR, "malformed URI sequence")
|
||||
MSG_DEF(JSMSG_INVALID_NORMALIZE_FORM, 0, JSEXN_RANGEERR, "form must be one of 'NFC', 'NFD', 'NFKC', or 'NFKD'")
|
||||
MSG_DEF(JSMSG_NEGATIVE_REPETITION_COUNT, 0, JSEXN_RANGEERR, "repeat count must be non-negative")
|
||||
MSG_DEF(JSMSG_NOT_A_CODEPOINT, 1, JSEXN_RANGEERR, "{0} is not a valid code point")
|
||||
MSG_DEF(JSMSG_RESULTING_STRING_TOO_LARGE, 0, JSEXN_RANGEERR, "repeat count must be less than infinity and not overflow maximum string size")
|
||||
|
||||
// Number
|
||||
MSG_DEF(JSMSG_BAD_RADIX, 0, JSEXN_RANGEERR, "radix must be an integer at least 2 and no greater than 36")
|
||||
MSG_DEF(JSMSG_PRECISION_RANGE, 1, JSEXN_RANGEERR, "precision {0} out of range")
|
||||
|
||||
// Function
|
||||
MSG_DEF(JSMSG_BAD_APPLY_ARGS, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array")
|
||||
MSG_DEF(JSMSG_BAD_FORMAL, 0, JSEXN_SYNTAXERR, "malformed formal parameter")
|
||||
MSG_DEF(JSMSG_CALLER_IS_STRICT, 0, JSEXN_TYPEERR, "access to strict mode caller function is censored")
|
||||
MSG_DEF(JSMSG_DEPRECATED_USAGE, 1, JSEXN_REFERENCEERR, "deprecated {0} usage")
|
||||
MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 1, JSEXN_TYPEERR, "{0} is not a scripted function")
|
||||
MSG_DEF(JSMSG_NO_REST_NAME, 0, JSEXN_SYNTAXERR, "no parameter name after ...")
|
||||
MSG_DEF(JSMSG_PARAMETER_AFTER_REST, 0, JSEXN_SYNTAXERR, "parameter after rest parameter")
|
||||
MSG_DEF(JSMSG_TOO_MANY_ARGUMENTS, 0, JSEXN_RANGEERR, "too many arguments provided for a function call")
|
||||
|
||||
// CSP
|
||||
MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 0, JSEXN_ERR, "call to eval() blocked by CSP")
|
||||
MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 0, JSEXN_ERR, "call to Function() blocked by CSP")
|
||||
|
||||
// Wrappers
|
||||
MSG_DEF(JSMSG_ACCESSOR_DEF_DENIED, 1, JSEXN_ERR, "Permission denied to define accessor property {0}")
|
||||
MSG_DEF(JSMSG_DEAD_OBJECT, 0, JSEXN_TYPEERR, "can't access dead object")
|
||||
MSG_DEF(JSMSG_UNWRAP_DENIED, 0, JSEXN_ERR, "permission denied to unwrap object")
|
||||
|
||||
// JSAPI-only (Not thrown as JS exceptions)
|
||||
MSG_DEF(JSMSG_BAD_CLONE_FUNOBJ_SCOPE, 0, JSEXN_TYPEERR, "bad cloned function scope chain")
|
||||
MSG_DEF(JSMSG_CANT_CLONE_OBJECT, 0, JSEXN_TYPEERR, "can't clone object")
|
||||
MSG_DEF(JSMSG_CANT_OPEN, 2, JSEXN_ERR, "can't open {0}: {1}")
|
||||
MSG_DEF(JSMSG_USER_DEFINED_ERROR, 0, JSEXN_ERR, "JS_ReportError was called")
|
||||
|
||||
// Internal errors
|
||||
MSG_DEF(JSMSG_ALLOC_OVERFLOW, 0, JSEXN_INTERNALERR, "allocation size overflow")
|
||||
MSG_DEF(JSMSG_BAD_BYTECODE, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}")
|
||||
MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 0, JSEXN_INTERNALERR, "buffer too small")
|
||||
MSG_DEF(JSMSG_BUILD_ID_NOT_AVAILABLE, 0, JSEXN_INTERNALERR, "build ID is not available")
|
||||
MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})")
|
||||
MSG_DEF(JSMSG_ERR_DURING_THROW, 0, JSEXN_INTERNALERR, "an internal error occurred while throwing an exception")
|
||||
MSG_DEF(JSMSG_NEED_DIET, 1, JSEXN_INTERNALERR, "{0} too large")
|
||||
MSG_DEF(JSMSG_OUT_OF_MEMORY, 0, JSEXN_INTERNALERR, "out of memory")
|
||||
MSG_DEF(JSMSG_OVER_RECURSED, 0, JSEXN_INTERNALERR, "too much recursion")
|
||||
MSG_DEF(JSMSG_TOO_BIG_TO_ENCODE, 0, JSEXN_INTERNALERR, "data are to big to encode")
|
||||
MSG_DEF(JSMSG_TOO_DEEP, 1, JSEXN_INTERNALERR, "{0} nested too deeply")
|
||||
MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 1, JSEXN_INTERNALERR, "uncaught exception: {0}")
|
||||
MSG_DEF(JSMSG_UNKNOWN_FORMAT, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}")
|
||||
|
||||
// Frontend
|
||||
MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}")
|
||||
MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
|
||||
MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initializer too large")
|
||||
MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *")
|
||||
MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
|
||||
MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async")
|
||||
MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression")
|
||||
MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
|
||||
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
|
||||
MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
|
||||
MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration")
|
||||
MSG_DEF(JSMSG_BAD_CONTINUE, 0, JSEXN_SYNTAXERR, "continue must be inside loop")
|
||||
MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 0, JSEXN_REFERENCEERR, "invalid destructuring assignment operator")
|
||||
MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET, 0, JSEXN_SYNTAXERR, "invalid destructuring target")
|
||||
MSG_DEF(JSMSG_BAD_DESTRUCT_PARENS, 0, JSEXN_SYNTAXERR, "destructuring patterns in assignments can't be parenthesized")
|
||||
MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration")
|
||||
MSG_DEF(JSMSG_BAD_DUP_ARGS, 0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context")
|
||||
MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loop")
|
||||
MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding")
|
||||
MSG_DEF(JSMSG_LET_STARTING_FOROF_LHS, 0, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with 'let'")
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0} returns a value")
|
||||
MSG_DEF(JSMSG_BAD_GENEXP_BODY, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
|
||||
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
|
||||
MSG_DEF(JSMSG_BAD_METHOD_DEF, 0, JSEXN_SYNTAXERR, "bad method definition")
|
||||
MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
|
||||
MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand")
|
||||
MSG_DEF(JSMSG_BAD_POW_LEFTSIDE, 0, JSEXN_SYNTAXERR, "unparenthesized unary expression can't appear on the left-hand side of '**'")
|
||||
MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id")
|
||||
MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function")
|
||||
MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code")
|
||||
MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch statement")
|
||||
MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
|
||||
MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
|
||||
MSG_DEF(JSMSG_BAD_SUPERCALL, 0, JSEXN_SYNTAXERR, "super() is only valid in derived class constructors")
|
||||
MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
|
||||
MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list")
|
||||
MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
|
||||
MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 0, JSEXN_SYNTAXERR, "catch after unconditional catch")
|
||||
MSG_DEF(JSMSG_CATCH_IDENTIFIER, 0, JSEXN_SYNTAXERR, "missing identifier in catch")
|
||||
MSG_DEF(JSMSG_CATCH_OR_FINALLY, 0, JSEXN_SYNTAXERR, "missing catch or finally after try")
|
||||
MSG_DEF(JSMSG_CATCH_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "catch without try")
|
||||
MSG_DEF(JSMSG_COLON_AFTER_CASE, 0, JSEXN_SYNTAXERR, "missing : after case label")
|
||||
MSG_DEF(JSMSG_COLON_AFTER_ID, 0, JSEXN_SYNTAXERR, "missing : after property id")
|
||||
MSG_DEF(JSMSG_COLON_IN_COND, 0, JSEXN_SYNTAXERR, "missing : in conditional expression")
|
||||
MSG_DEF(JSMSG_COMP_PROP_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing ] in computed property name")
|
||||
MSG_DEF(JSMSG_CONTRARY_NONDIRECTIVE, 1, JSEXN_SYNTAXERR, "'{0}' statement won't be enforced as a directive because it isn't in directive prologue position")
|
||||
MSG_DEF(JSMSG_CURLY_AFTER_BODY, 0, JSEXN_SYNTAXERR, "missing } after function body")
|
||||
MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 0, JSEXN_SYNTAXERR, "missing } after catch block")
|
||||
MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 0, JSEXN_SYNTAXERR, "missing } after finally block")
|
||||
MSG_DEF(JSMSG_CURLY_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing } after property list")
|
||||
MSG_DEF(JSMSG_CURLY_AFTER_TRY, 0, JSEXN_SYNTAXERR, "missing } after try block")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_BODY, 0, JSEXN_SYNTAXERR, "missing { before function body")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_CATCH, 0, JSEXN_SYNTAXERR, "missing { before catch block")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_CLASS, 0, JSEXN_SYNTAXERR, "missing { before class body")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY, 0, JSEXN_SYNTAXERR, "missing { before finally block")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH, 0, JSEXN_SYNTAXERR, "missing { before switch body")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_TRY, 0, JSEXN_SYNTAXERR, "missing { before try block")
|
||||
MSG_DEF(JSMSG_CURLY_IN_COMPOUND, 0, JSEXN_SYNTAXERR, "missing } in compound statement")
|
||||
MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT,0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword")
|
||||
MSG_DEF(JSMSG_DECLARATION_AFTER_IMPORT,0, JSEXN_SYNTAXERR, "missing declaration after 'import' keyword")
|
||||
MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 0, JSEXN_SYNTAXERR, "applying the 'delete' operator to an unqualified name is deprecated")
|
||||
MSG_DEF(JSMSG_DEPRECATED_EXPR_CLOSURE, 0, JSEXN_WARN, "expression closures are deprecated")
|
||||
MSG_DEF(JSMSG_DEPRECATED_FOR_EACH, 0, JSEXN_WARN, "JavaScript 1.6's for-each-in loops are deprecated; consider using ES6 for-of instead")
|
||||
MSG_DEF(JSMSG_DEPRECATED_OCTAL, 0, JSEXN_SYNTAXERR, "\"0\"-prefixed octal literals and octal escape sequences are deprecated; for octal literals use the \"0o\" prefix instead")
|
||||
MSG_DEF(JSMSG_DEPRECATED_PRAGMA, 1, JSEXN_WARN, "Using //@ to indicate {0} pragmas is deprecated. Use //# instead")
|
||||
MSG_DEF(JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL, 1, JSEXN_WARN, "redeclaration of block-scoped function `{0}' is deprecated")
|
||||
MSG_DEF(JSMSG_DUPLICATE_EXPORT_NAME, 1, JSEXN_SYNTAXERR, "duplicate export name '{0}'")
|
||||
MSG_DEF(JSMSG_DUPLICATE_FORMAL, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}")
|
||||
MSG_DEF(JSMSG_DUPLICATE_LABEL, 0, JSEXN_SYNTAXERR, "duplicate label")
|
||||
MSG_DEF(JSMSG_DUPLICATE_PROPERTY, 1, JSEXN_SYNTAXERR, "property name {0} appears more than once in object literal")
|
||||
MSG_DEF(JSMSG_DUPLICATE_PROTO_PROPERTY, 0, JSEXN_SYNTAXERR, "property name __proto__ appears more than once in object literal")
|
||||
MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?")
|
||||
MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?")
|
||||
MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0, JSEXN_SYNTAXERR, "export declarations may only appear at top level of a module")
|
||||
MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "finally without try")
|
||||
MSG_DEF(JSMSG_FORBIDDEN_AS_STATEMENT, 1, JSEXN_SYNTAXERR, "{0} can't appear in single-statement context")
|
||||
MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause")
|
||||
MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
|
||||
MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
|
||||
MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
|
||||
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character")
|
||||
MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
|
||||
MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
|
||||
MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found")
|
||||
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
|
||||
MSG_DEF(JSMSG_GENERATOR_LABEL, 0, JSEXN_SYNTAXERR, "generator functions cannot be labelled")
|
||||
MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled")
|
||||
MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
|
||||
MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
|
||||
MSG_DEF(JSMSG_LINE_BREAK_BEFORE_ARROW, 0, JSEXN_SYNTAXERR, "no line break is allowed before '=>'")
|
||||
MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
|
||||
MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
|
||||
MSG_DEF(JSMSG_MISSING_EXPONENT, 0, JSEXN_SYNTAXERR, "missing exponent")
|
||||
MSG_DEF(JSMSG_MISSING_EXPR_AFTER_THROW,0, JSEXN_SYNTAXERR, "throw statement is missing an expression")
|
||||
MSG_DEF(JSMSG_MISSING_FORMAL, 0, JSEXN_SYNTAXERR, "missing formal parameter")
|
||||
MSG_DEF(JSMSG_MISSING_HEXDIGITS, 0, JSEXN_SYNTAXERR, "missing hexadecimal digits after '0x'")
|
||||
MSG_DEF(JSMSG_MISSING_OCTAL_DIGITS, 0, JSEXN_SYNTAXERR, "missing octal digits after '0o'")
|
||||
MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM, 0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword")
|
||||
MSG_DEF(JSMSG_NAME_AFTER_DOT, 0, JSEXN_SYNTAXERR, "missing name after . operator")
|
||||
MSG_DEF(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT, 0, JSEXN_SYNTAXERR, "expected named imports or namespace import after comma")
|
||||
MSG_DEF(JSMSG_NO_BINDING_NAME, 0, JSEXN_SYNTAXERR, "missing binding name")
|
||||
MSG_DEF(JSMSG_NO_EXPORT_NAME, 0, JSEXN_SYNTAXERR, "missing export name")
|
||||
MSG_DEF(JSMSG_NO_IMPORT_NAME, 0, JSEXN_SYNTAXERR, "missing import name")
|
||||
MSG_DEF(JSMSG_NO_VARIABLE_NAME, 0, JSEXN_SYNTAXERR, "missing variable name")
|
||||
MSG_DEF(JSMSG_OF_AFTER_FOR_NAME, 0, JSEXN_SYNTAXERR, "missing 'of' after for")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_ARGS, 0, JSEXN_SYNTAXERR, "missing ) after argument list")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_CATCH, 0, JSEXN_SYNTAXERR, "missing ) after catch")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_COND, 0, JSEXN_SYNTAXERR, "missing ) after condition")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_FOR, 0, JSEXN_SYNTAXERR, "missing ( after for")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL, 0, JSEXN_SYNTAXERR, "missing ) after for-loop control")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_FOR_OF_ITERABLE, 0, JSEXN_SYNTAXERR, "missing ) after for-of iterable")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_SWITCH, 0, JSEXN_SYNTAXERR, "missing ) after switch expression")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_WITH, 0, JSEXN_SYNTAXERR, "missing ) after with-statement object")
|
||||
MSG_DEF(JSMSG_PAREN_BEFORE_CATCH, 0, JSEXN_SYNTAXERR, "missing ( before catch")
|
||||
MSG_DEF(JSMSG_PAREN_BEFORE_COND, 0, JSEXN_SYNTAXERR, "missing ( before condition")
|
||||
MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters")
|
||||
MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH, 0, JSEXN_SYNTAXERR, "missing ( before switch expression")
|
||||
MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 0, JSEXN_SYNTAXERR, "missing ( before with-statement object")
|
||||
MSG_DEF(JSMSG_PAREN_IN_PAREN, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical")
|
||||
MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list")
|
||||
MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after module specifier list")
|
||||
MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_SYNTAXERR, "redeclaration of identifier '{0}' in catch")
|
||||
MSG_DEF(JSMSG_RESERVED_ID, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier")
|
||||
MSG_DEF(JSMSG_REST_WITH_COMMA, 0, JSEXN_SYNTAXERR, "rest element may not have a trailing comma")
|
||||
MSG_DEF(JSMSG_REST_WITH_DEFAULT, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
|
||||
MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, 1, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level {0} declarations")
|
||||
MSG_DEF(JSMSG_SELFHOSTED_METHOD_CALL, 0, JSEXN_SYNTAXERR, "self-hosted code may not contain direct method calls. Use callFunction() or callContentFunction()")
|
||||
MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
|
||||
MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition")
|
||||
MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")
|
||||
MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 0, JSEXN_SYNTAXERR, "missing ; before statement")
|
||||
MSG_DEF(JSMSG_SOURCE_TOO_LONG, 0, JSEXN_RANGEERR, "source is too long")
|
||||
MSG_DEF(JSMSG_STMT_AFTER_RETURN, 0, JSEXN_WARN, "unreachable code after return statement")
|
||||
MSG_DEF(JSMSG_STRICT_CODE_WITH, 0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements")
|
||||
MSG_DEF(JSMSG_STRICT_NON_SIMPLE_PARAMS, 1, JSEXN_SYNTAXERR, "\"use strict\" not allowed in function with {0} parameter")
|
||||
MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing } in template string")
|
||||
MSG_DEF(JSMSG_SIMD_NOT_A_VECTOR, 2, JSEXN_TYPEERR, "expecting a SIMD {0} object as argument {1}")
|
||||
MSG_DEF(JSMSG_TOO_MANY_CASES, 0, JSEXN_INTERNALERR, "too many switch cases")
|
||||
MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 0, JSEXN_SYNTAXERR, "too many catch variables")
|
||||
MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 0, JSEXN_SYNTAXERR, "too many constructor arguments")
|
||||
MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 0, JSEXN_SYNTAXERR, "more than one switch default")
|
||||
MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 0, JSEXN_SYNTAXERR, "too many function arguments")
|
||||
MSG_DEF(JSMSG_TOO_MANY_LOCALS, 0, JSEXN_SYNTAXERR, "too many local variables")
|
||||
MSG_DEF(JSMSG_TOO_MANY_YIELDS, 0, JSEXN_SYNTAXERR, "too many yield expressions")
|
||||
MSG_DEF(JSMSG_TOUGH_BREAK, 0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch")
|
||||
MSG_DEF(JSMSG_UNEXPECTED_TOKEN, 2, JSEXN_SYNTAXERR, "expected {0}, got {1}")
|
||||
MSG_DEF(JSMSG_UNNAMED_CLASS_STMT, 0, JSEXN_SYNTAXERR, "class statement requires a name")
|
||||
MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 0, JSEXN_SYNTAXERR, "function statement requires a name")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 0, JSEXN_SYNTAXERR, "unterminated comment")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_STRING, 0, JSEXN_SYNTAXERR, "unterminated string literal")
|
||||
MSG_DEF(JSMSG_USELESS_EXPR, 0, JSEXN_TYPEERR, "useless expression")
|
||||
MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 0, JSEXN_SYNTAXERR, "\"use asm\" is only meaningful in the Directive Prologue of a function body")
|
||||
MSG_DEF(JSMSG_VAR_HIDES_ARG, 1, JSEXN_TYPEERR, "variable {0} redeclares argument")
|
||||
MSG_DEF(JSMSG_WHILE_AFTER_DO, 0, JSEXN_SYNTAXERR, "missing while after do-loop body")
|
||||
MSG_DEF(JSMSG_YIELD_IN_ARROW, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield")
|
||||
MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "yield in default expression")
|
||||
MSG_DEF(JSMSG_YIELD_IN_METHOD, 0, JSEXN_SYNTAXERR, "non-generator method definitions may not contain yield")
|
||||
MSG_DEF(JSMSG_BAD_COLUMN_NUMBER, 0, JSEXN_RANGEERR, "column number out of range")
|
||||
MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property names aren't supported in this destructuring declaration")
|
||||
MSG_DEF(JSMSG_DEFAULT_IN_PATTERN, 0, JSEXN_SYNTAXERR, "destructuring defaults aren't supported in this destructuring declaration")
|
||||
MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allowed within functions")
|
||||
MSG_DEF(JSMSG_ESCAPED_KEYWORD, 0, JSEXN_SYNTAXERR, "keywords must be written literally, without embedded escapes")
|
||||
|
||||
// asm.js
|
||||
MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
|
||||
MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
|
||||
MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 1, JSEXN_WARN, "Successfully compiled asm.js code ({0})")
|
||||
|
||||
// wasm
|
||||
MSG_DEF(JSMSG_WASM_COMPILE_ERROR, 1, JSEXN_WASMCOMPILEERROR, "{0}")
|
||||
MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL, 0, JSEXN_WASMRUNTIMEERROR, "indirect call to null")
|
||||
MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG, 0, JSEXN_WASMRUNTIMEERROR, "indirect call signature mismatch")
|
||||
MSG_DEF(JSMSG_WASM_UNREACHABLE, 0, JSEXN_WASMRUNTIMEERROR, "unreachable executed")
|
||||
MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW, 0, JSEXN_WASMRUNTIMEERROR, "integer overflow")
|
||||
MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_WASMRUNTIMEERROR, "invalid conversion to integer")
|
||||
MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_WASMRUNTIMEERROR, "integer divide by zero")
|
||||
MSG_DEF(JSMSG_WASM_OUT_OF_BOUNDS, 0, JSEXN_WASMRUNTIMEERROR, "index out of bounds")
|
||||
MSG_DEF(JSMSG_WASM_UNALIGNED_ACCESS, 0, JSEXN_WASMRUNTIMEERROR, "unaligned memory access")
|
||||
MSG_DEF(JSMSG_WASM_BAD_UINT32, 2, JSEXN_RANGEERR, "bad {0} {1}")
|
||||
MSG_DEF(JSMSG_WASM_BAD_GROW, 1, JSEXN_RANGEERR, "failed to grow {0}")
|
||||
MSG_DEF(JSMSG_WASM_BAD_FIT, 2, JSEXN_RANGEERR, "{0} segment does not fit in {1}")
|
||||
MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument must be an ArrayBuffer or typed array object")
|
||||
MSG_DEF(JSMSG_WASM_BAD_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module")
|
||||
MSG_DEF(JSMSG_WASM_BAD_BUF_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module, ArrayBuffer or typed array object")
|
||||
MSG_DEF(JSMSG_WASM_BAD_DESC_ARG, 1, JSEXN_TYPEERR, "first argument must be a {0} descriptor")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE, 1, JSEXN_TYPEERR, "imported {0} with incompatible size")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMP_MAX, 1, JSEXN_TYPEERR, "imported {0} with incompatible maximum size")
|
||||
MSG_DEF(JSMSG_WASM_BAD_ELEMENT, 0, JSEXN_TYPEERR, "\"element\" property of table descriptor must be \"anyfunc\"")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument must be an object")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD, 2, JSEXN_TYPEERR, "import object field '{0}' is not {1}")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG, 0, JSEXN_TYPEERR, "imported function signature mismatch")
|
||||
MSG_DEF(JSMSG_WASM_BAD_TABLE_VALUE, 0, JSEXN_TYPEERR, "can only assign WebAssembly exported functions to Table")
|
||||
MSG_DEF(JSMSG_WASM_BAD_I64, 0, JSEXN_TYPEERR, "cannot pass i64 to or from JS")
|
||||
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
|
||||
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
|
||||
|
||||
// Proxy
|
||||
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
|
||||
MSG_DEF(JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler returned a non-object, non-null value")
|
||||
MSG_DEF(JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler didn't return the target object's prototype")
|
||||
MSG_DEF(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy setPrototypeOf handler returned false")
|
||||
MSG_DEF(JSMSG_PROXY_ISEXTENSIBLE_RETURNED_FALSE,0,JSEXN_TYPEERR,"proxy isExtensible handler must return the same extensibility as target")
|
||||
MSG_DEF(JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy setPrototypeOf handler returned true, even though the target's prototype is immutable because the target is non-extensible")
|
||||
MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 0, JSEXN_TYPEERR, "can't change object's extensibility")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_INVALID, 0, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_NEW, 0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC, 0, JSEXN_TYPEERR, "proxy can't define a non-existent property as non-configurable")
|
||||
MSG_DEF(JSMSG_PROXY_DEFINE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy defineProperty handler returned false for property '{0}'")
|
||||
MSG_DEF(JSMSG_PROXY_DELETE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "can't delete property '{0}': proxy deleteProperty handler returned false")
|
||||
MSG_DEF(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy preventExtensions handler returned false")
|
||||
MSG_DEF(JSMSG_PROXY_SET_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy set handler returned false for property '{0}'")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE, 0, JSEXN_TYPEERR, "proxy can't report an extensible object as non-extensible")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_C_AS_NC, 0, JSEXN_TYPEERR, "proxy can't report existing configurable property as non-configurable")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_INVALID, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NEW, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC, 0, JSEXN_TYPEERR, "proxy can't report a non-existent property as non-configurable")
|
||||
MSG_DEF(JSMSG_CANT_SET_NW_NC, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property")
|
||||
MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter")
|
||||
MSG_DEF(JSMSG_CANT_SKIP_NC, 0, JSEXN_TYPEERR, "proxy can't skip a non-configurable property")
|
||||
MSG_DEF(JSMSG_ONWKEYS_STR_SYM, 0, JSEXN_TYPEERR, "proxy [[OwnPropertyKeys]] must return an array with only string and symbol elements")
|
||||
MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 0, JSEXN_TYPEERR, "proxy must report the same value for a non-writable, non-configurable property")
|
||||
MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 0, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property without a getter")
|
||||
MSG_DEF(JSMSG_OBJECT_ACCESS_DENIED, 0, JSEXN_ERR, "Permission denied to access object")
|
||||
MSG_DEF(JSMSG_PROPERTY_ACCESS_DENIED, 1, JSEXN_ERR, "Permission denied to access property {0}")
|
||||
MSG_DEF(JSMSG_PROXY_CONSTRUCT_OBJECT, 0, JSEXN_TYPEERR, "proxy [[Construct]] must return an object")
|
||||
MSG_DEF(JSMSG_PROXY_EXTENSIBILITY, 0, JSEXN_TYPEERR, "proxy must report same extensiblitity as target")
|
||||
MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF, 0, JSEXN_TYPEERR, "proxy [[GetOwnProperty]] must return an object or undefined")
|
||||
MSG_DEF(JSMSG_PROXY_REVOKED, 0, JSEXN_TYPEERR, "illegal operation attempted on a revoked proxy")
|
||||
MSG_DEF(JSMSG_PROXY_ARG_REVOKED, 1, JSEXN_TYPEERR, "argument {0} cannot be a revoked proxy")
|
||||
MSG_DEF(JSMSG_BAD_TRAP, 1, JSEXN_TYPEERR, "proxy handler's {0} trap wasn't undefined, null, or callable")
|
||||
|
||||
// Structured cloning
|
||||
MSG_DEF(JSMSG_SC_BAD_CLONE_VERSION, 0, JSEXN_ERR, "unsupported structured clone version")
|
||||
MSG_DEF(JSMSG_SC_BAD_SERIALIZED_DATA, 1, JSEXN_INTERNALERR, "bad serialized structured data ({0})")
|
||||
MSG_DEF(JSMSG_SC_DUP_TRANSFERABLE, 0, JSEXN_TYPEERR, "duplicate transferable for structured clone")
|
||||
MSG_DEF(JSMSG_SC_NOT_TRANSFERABLE, 0, JSEXN_TYPEERR, "invalid transferable array for structured clone")
|
||||
MSG_DEF(JSMSG_SC_UNSUPPORTED_TYPE, 0, JSEXN_TYPEERR, "unsupported type for structured data")
|
||||
MSG_DEF(JSMSG_SC_NOT_CLONABLE, 1, JSEXN_TYPEERR, "{0} cannot be cloned in this context")
|
||||
MSG_DEF(JSMSG_SC_SAB_TRANSFER, 0, JSEXN_WARN, "SharedArrayBuffer must not be in the transfer list")
|
||||
MSG_DEF(JSMSG_SC_SAB_DISABLED, 0, JSEXN_TYPEERR, "SharedArrayBuffer not cloned - shared memory disabled in receiver")
|
||||
|
||||
// Debugger
|
||||
MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_AWAIT, 0, JSEXN_TYPEERR, "await expression received invalid value")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_LINE, 0, JSEXN_TYPEERR, "invalid line number")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 0, JSEXN_TYPEERR, "invalid script offset")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_RESUMPTION, 0, JSEXN_TYPEERR, "debugger resumption value must be undefined, {throw: val}, {return: val}, or null")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_YIELD, 0, JSEXN_TYPEERR, "generator yielded invalid value")
|
||||
MSG_DEF(JSMSG_DEBUG_CANT_DEBUG_GLOBAL, 0, JSEXN_TYPEERR, "passing non-debuggable global to addDebuggee")
|
||||
MSG_DEF(JSMSG_DEBUG_CCW_REQUIRED, 1, JSEXN_TYPEERR, "{0}: argument must be an object from a different compartment")
|
||||
MSG_DEF(JSMSG_DEBUG_COMPARTMENT_MISMATCH, 2, JSEXN_TYPEERR, "{0}: descriptor .{1} property is an object in a different compartment than the target object")
|
||||
MSG_DEF(JSMSG_DEBUG_LOOP, 0, JSEXN_TYPEERR, "cannot debug an object in same compartment as debugger or a compartment that is already debugging the debugger")
|
||||
MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGEE, 2, JSEXN_ERR, "{0} is not a debuggee {1}")
|
||||
MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGING, 0, JSEXN_ERR, "can't set breakpoint: script global is not a debuggee")
|
||||
MSG_DEF(JSMSG_DEBUG_NOT_IDLE, 0, JSEXN_ERR, "can't start debugging: a debuggee script is on the stack")
|
||||
MSG_DEF(JSMSG_DEBUG_NOT_LIVE, 1, JSEXN_ERR, "{0} is not live")
|
||||
MSG_DEF(JSMSG_DEBUG_NO_ENV_OBJECT, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects")
|
||||
MSG_DEF(JSMSG_DEBUG_PROTO, 2, JSEXN_TYPEERR, "{0}.prototype is not a valid {1} instance")
|
||||
MSG_DEF(JSMSG_DEBUG_WRONG_OWNER, 1, JSEXN_TYPEERR, "{0} belongs to a different Debugger")
|
||||
MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT, 1, JSEXN_ERR, "variable `{0}' has been optimized out")
|
||||
MSG_DEF(JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED, 0, JSEXN_TYPEERR, "resumption values are disallowed in this hook")
|
||||
MSG_DEF(JSMSG_DEBUG_VARIABLE_NOT_FOUND,0, JSEXN_TYPEERR, "variable not found in environment")
|
||||
MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY, 3, JSEXN_TYPEERR, "{0} is {1}{2}a global object, but a direct reference is required")
|
||||
MSG_DEF(JSMSG_DEBUGGEE_WOULD_RUN, 2, JSEXN_DEBUGGEEWOULDRUN, "debuggee `{0}:{1}' would run")
|
||||
MSG_DEF(JSMSG_NOT_CALLABLE_OR_UNDEFINED, 0, JSEXN_TYPEERR, "value is not a function or undefined")
|
||||
MSG_DEF(JSMSG_NOT_TRACKING_ALLOCATIONS, 1, JSEXN_ERR, "Cannot call {0} without setting trackingAllocationSites to true")
|
||||
MSG_DEF(JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET, 0, JSEXN_ERR, "Cannot track object allocation, because other tools are already doing so")
|
||||
MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 0, JSEXN_TYPEERR, "findScripts query object with 'innermost' property must have 'line' and either 'displayURL', 'url', or 'source'")
|
||||
MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL, 0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'displayURL', 'url', or 'source' property")
|
||||
MSG_DEF(JSMSG_DEBUG_CANT_SET_OPT_ENV, 1, JSEXN_REFERENCEERR, "can't set `{0}' in an optimized-out environment")
|
||||
MSG_DEF(JSMSG_DEBUG_INVISIBLE_COMPARTMENT, 0, JSEXN_TYPEERR, "object in compartment marked as invisible to Debugger")
|
||||
MSG_DEF(JSMSG_DEBUG_CENSUS_BREAKDOWN, 1, JSEXN_TYPEERR, "unrecognized 'by' value in takeCensus breakdown: {0}")
|
||||
MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_RESOLVED, 0, JSEXN_TYPEERR, "Promise hasn't been resolved")
|
||||
MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_FULFILLED, 0, JSEXN_TYPEERR, "Promise hasn't been fulfilled")
|
||||
MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_REJECTED, 0, JSEXN_TYPEERR, "Promise hasn't been rejected")
|
||||
|
||||
// Tracelogger
|
||||
MSG_DEF(JSMSG_TRACELOGGER_ENABLE_FAIL, 1, JSEXN_ERR, "enabling tracelogger failed: {0}")
|
||||
|
||||
// Intl
|
||||
MSG_DEF(JSMSG_DATE_NOT_FINITE, 0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()")
|
||||
MSG_DEF(JSMSG_INTERNAL_INTL_ERROR, 0, JSEXN_ERR, "internal error while computing Intl data")
|
||||
MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED, 3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1} called on value that's not an object initialized as a {2}")
|
||||
MSG_DEF(JSMSG_INTL_OBJECT_REINITED, 0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor")
|
||||
MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
|
||||
MSG_DEF(JSMSG_INVALID_DIGITS_VALUE, 1, JSEXN_RANGEERR, "invalid digits value: {0}")
|
||||
MSG_DEF(JSMSG_INVALID_LANGUAGE_TAG, 1, JSEXN_RANGEERR, "invalid language tag: {0}")
|
||||
MSG_DEF(JSMSG_INVALID_LOCALES_ELEMENT, 0, JSEXN_TYPEERR, "invalid element in locales argument")
|
||||
MSG_DEF(JSMSG_INVALID_LOCALE_MATCHER, 1, JSEXN_RANGEERR, "invalid locale matcher in supportedLocalesOf(): {0}")
|
||||
MSG_DEF(JSMSG_INVALID_OPTION_VALUE, 2, JSEXN_RANGEERR, "invalid value {1} for option {0}")
|
||||
MSG_DEF(JSMSG_INVALID_TIME_ZONE, 1, JSEXN_RANGEERR, "invalid time zone in DateTimeFormat(): {0}")
|
||||
MSG_DEF(JSMSG_UNDEFINED_CURRENCY, 0, JSEXN_TYPEERR, "undefined currency in NumberFormat() with currency style")
|
||||
|
||||
// RegExp
|
||||
MSG_DEF(JSMSG_BACK_REF_OUT_OF_RANGE, 0, JSEXN_SYNTAXERR, "back reference out of range in regular expression")
|
||||
MSG_DEF(JSMSG_BAD_CLASS_RANGE, 0, JSEXN_SYNTAXERR, "invalid range in character class")
|
||||
MSG_DEF(JSMSG_ESCAPE_AT_END_OF_REGEXP, 0, JSEXN_SYNTAXERR, "\\ at end of pattern")
|
||||
MSG_DEF(JSMSG_EXEC_NOT_OBJORNULL, 0, JSEXN_TYPEERR, "RegExp exec method should return object or null")
|
||||
MSG_DEF(JSMSG_INVALID_DECIMAL_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid decimal escape in regular expression")
|
||||
MSG_DEF(JSMSG_INVALID_GROUP, 0, JSEXN_SYNTAXERR, "invalid regexp group")
|
||||
MSG_DEF(JSMSG_INVALID_IDENTITY_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid identity escape in regular expression")
|
||||
MSG_DEF(JSMSG_INVALID_UNICODE_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid unicode escape in regular expression")
|
||||
MSG_DEF(JSMSG_MISSING_PAREN, 0, JSEXN_SYNTAXERR, "unterminated parenthetical")
|
||||
MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another")
|
||||
MSG_DEF(JSMSG_NOTHING_TO_REPEAT, 0, JSEXN_SYNTAXERR, "nothing to repeat")
|
||||
MSG_DEF(JSMSG_NUMBERS_OUT_OF_ORDER, 0, JSEXN_SYNTAXERR, "numbers out of order in {} quantifier.")
|
||||
MSG_DEF(JSMSG_RANGE_WITH_CLASS_ESCAPE, 0, JSEXN_SYNTAXERR, "character class escape cannot be used in class range in regular expression")
|
||||
MSG_DEF(JSMSG_RAW_BRACE_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw brace is not allowed in regular expression with unicode flag")
|
||||
MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag")
|
||||
MSG_DEF(JSMSG_TOO_MANY_PARENS, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
|
||||
MSG_DEF(JSMSG_UNICODE_OVERFLOW, 0, JSEXN_SYNTAXERR, "unicode codepoint should not be greater than 0x10FFFF in regular expression")
|
||||
MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
|
||||
MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated character class")
|
||||
|
||||
// Self-hosting
|
||||
MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR, 0, JSEXN_ERR, "internal error getting the default locale")
|
||||
MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1, JSEXN_ERR, "No such property on self-hosted object: {0}")
|
||||
|
||||
// Typed object / SIMD
|
||||
MSG_DEF(JSMSG_INVALID_PROTOTYPE, 0, JSEXN_TYPEERR, "prototype field is not an object")
|
||||
MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS, 0, JSEXN_TYPEERR, "invalid arguments")
|
||||
MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
|
||||
MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
|
||||
MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
|
||||
MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG, 0, JSEXN_ERR, "Type is too large to allocate")
|
||||
MSG_DEF(JSMSG_SIMD_FAILED_CONVERSION, 0, JSEXN_RANGEERR, "SIMD conversion loses precision")
|
||||
MSG_DEF(JSMSG_SIMD_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert SIMD value to number")
|
||||
|
||||
// Array
|
||||
MSG_DEF(JSMSG_TOO_LONG_ARRAY, 0, JSEXN_TYPEERR, "Too long array")
|
||||
|
||||
// Typed array
|
||||
MSG_DEF(JSMSG_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
|
||||
MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
|
||||
MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")
|
||||
MSG_DEF(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected ArrayBuffer with at least {0} bytes, but species constructor returns ArrayBuffer with {1} bytes")
|
||||
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 0, JSEXN_TYPEERR, "invalid arguments")
|
||||
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
|
||||
MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED, 0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
|
||||
MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS, 0, JSEXN_RANGEERR, "attempting to construct out-of-bounds TypedArray on ArrayBuffer")
|
||||
MSG_DEF(JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT, 1, JSEXN_TYPEERR, "cannot directly {0} builtin %TypedArray%")
|
||||
MSG_DEF(JSMSG_NON_TYPED_ARRAY_RETURNED, 0, JSEXN_TYPEERR, "constructor didn't return TypedArray object")
|
||||
MSG_DEF(JSMSG_SHORT_TYPED_ARRAY_RETURNED, 2, JSEXN_TYPEERR, "expected TypedArray of at least length {0}, but constructor returned TypedArray of length {1}")
|
||||
|
||||
// Shared array buffer
|
||||
MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH, 0, JSEXN_RANGEERR, "length argument out of range")
|
||||
MSG_DEF(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected SharedArrayBuffer, but species constructor returned non-SharedArrayBuffer")
|
||||
MSG_DEF(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different SharedArrayBuffer, but species constructor returned same SharedArrayBuffer")
|
||||
MSG_DEF(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected SharedArrayBuffer with at least {0} bytes, but species constructor returns SharedArrayBuffer with {1} bytes")
|
||||
|
||||
// Reflect
|
||||
MSG_DEF(JSMSG_BAD_PARSE_NODE, 0, JSEXN_INTERNALERR, "bad parse node")
|
||||
|
||||
// Symbol
|
||||
MSG_DEF(JSMSG_SYMBOL_TO_STRING, 0, JSEXN_TYPEERR, "can't convert symbol to string")
|
||||
MSG_DEF(JSMSG_SYMBOL_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert symbol to number")
|
||||
|
||||
// Atomics and futexes
|
||||
MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY, 0, JSEXN_TYPEERR, "invalid array type for the operation")
|
||||
MSG_DEF(JSMSG_ATOMICS_TOO_LONG, 0, JSEXN_RANGEERR, "timeout value too large")
|
||||
MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED, 0, JSEXN_ERR, "waiting is not allowed on this thread")
|
||||
|
||||
// XPConnect wrappers and DOM bindings
|
||||
MSG_DEF(JSMSG_CANT_SET_INTERPOSED, 1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't define elements on a Window object")
|
||||
MSG_DEF(JSMSG_CANT_DELETE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't delete elements from a Window object")
|
||||
MSG_DEF(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't delete property {0} from window's named properties object")
|
||||
MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS, 0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
|
||||
MSG_DEF(JSMSG_NO_NAMED_SETTER, 2, JSEXN_TYPEERR, "{0} doesn't have a named property setter for '{1}'")
|
||||
MSG_DEF(JSMSG_NO_INDEXED_SETTER, 2, JSEXN_TYPEERR, "{0} doesn't have an indexed property setter for '{1}'")
|
||||
|
||||
// Super
|
||||
MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
|
||||
MSG_DEF(JSMSG_REINIT_THIS, 0, JSEXN_REFERENCEERR, "super() called twice in derived class constructor")
|
||||
|
||||
// Modules
|
||||
MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT, 0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
|
||||
MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT, 1, JSEXN_SYNTAXERR, "indirect export '{0}' not found")
|
||||
MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 1, JSEXN_SYNTAXERR, "ambiguous indirect export '{0}'")
|
||||
MSG_DEF(JSMSG_MISSING_IMPORT, 1, JSEXN_SYNTAXERR, "import '{0}' not found")
|
||||
MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 1, JSEXN_SYNTAXERR, "ambiguous import '{0}'")
|
||||
MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found for namespace")
|
||||
MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
|
||||
MSG_DEF(JSMSG_MODULE_INSTANTIATE_FAILED, 0, JSEXN_INTERNALERR, "attempt to re-instantiate module after failure")
|
||||
MSG_DEF(JSMSG_BAD_MODULE_STATE, 0, JSEXN_INTERNALERR, "module record in unexpected state")
|
||||
|
||||
// Promise
|
||||
MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.")
|
||||
MSG_DEF(JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.")
|
||||
MSG_DEF(JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
|
||||
MSG_DEF(JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
|
||||
MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "Promise rejection value is a non-unwrappable cross-compartment wrapper.")
|
||||
|
|
@ -1,369 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/*
|
||||
* Helper classes encapsulating access to the callee, |this| value, arguments,
|
||||
* and argument count for a call/construct operation.
|
||||
*
|
||||
* JS::CallArgs encapsulates access to a JSNative's un-abstracted
|
||||
* |unsigned argc, Value* vp| arguments. The principal way to create a
|
||||
* JS::CallArgs is using JS::CallArgsFromVp:
|
||||
*
|
||||
* // If provided no arguments or a non-numeric first argument, return zero.
|
||||
* // Otherwise return |this| exactly as given, without boxing.
|
||||
* static bool
|
||||
* Func(JSContext* cx, unsigned argc, JS::Value* vp)
|
||||
* {
|
||||
* JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
*
|
||||
* // Guard against no arguments or a non-numeric arg0.
|
||||
* if (args.length() == 0 || !args[0].isNumber()) {
|
||||
* args.rval().setInt32(0);
|
||||
* return true;
|
||||
* }
|
||||
*
|
||||
* // Access to the callee must occur before accessing/setting
|
||||
* // the return value.
|
||||
* JSObject& callee = args.callee();
|
||||
* args.rval().setObject(callee);
|
||||
*
|
||||
* // callee() and calleev() will now assert.
|
||||
*
|
||||
* // It's always fine to access thisv().
|
||||
* HandleValue thisv = args.thisv();
|
||||
* args.rval().set(thisv);
|
||||
*
|
||||
* // As the return value was last set to |this|, returns |this|.
|
||||
* return true;
|
||||
* }
|
||||
*
|
||||
* CallArgs is exposed publicly and used internally. Not all parts of its
|
||||
* public interface are meant to be used by embedders! See inline comments to
|
||||
* for details.
|
||||
*
|
||||
* It's possible (albeit deprecated) to manually index into |vp| to access the
|
||||
* callee, |this|, and arguments of a function, and to set its return value.
|
||||
* It's also possible to use the supported API of JS_CALLEE, JS_THIS, JS_ARGV,
|
||||
* JS_RVAL, and JS_SET_RVAL to the same ends.
|
||||
*
|
||||
* But neither API has the error-handling or moving-GC correctness of CallArgs.
|
||||
* New code should use CallArgs instead whenever possible.
|
||||
*
|
||||
* The eventual plan is to change JSNative to take |const CallArgs&| directly,
|
||||
* for automatic assertion of correct use and to make calling functions more
|
||||
* efficient. Embedders should start internally switching away from using
|
||||
* |argc| and |vp| directly, except to create a |CallArgs|. Then, when an
|
||||
* eventual release making that change occurs, porting efforts will require
|
||||
* changing methods' signatures but won't require invasive changes to the
|
||||
* methods' implementations, potentially under time pressure.
|
||||
*/
|
||||
|
||||
#ifndef js_CallArgs_h
|
||||
#define js_CallArgs_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Value.h"
|
||||
|
||||
/* Typedef for native functions called by the JS VM. */
|
||||
typedef bool
|
||||
(* JSNative)(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
namespace JS {
|
||||
|
||||
extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* Compute |this| for the |vp| inside a JSNative, either boxing primitives or
|
||||
* replacing with the global object as necessary.
|
||||
*/
|
||||
extern JS_PUBLIC_API(Value)
|
||||
ComputeThis(JSContext* cx, JS::Value* vp);
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
extern JS_PUBLIC_API(void)
|
||||
CheckIsValidConstructible(const Value& v);
|
||||
#endif
|
||||
|
||||
class MOZ_STACK_CLASS IncludeUsedRval
|
||||
{
|
||||
protected:
|
||||
#ifdef JS_DEBUG
|
||||
mutable bool usedRval_;
|
||||
void setUsedRval() const { usedRval_ = true; }
|
||||
void clearUsedRval() const { usedRval_ = false; }
|
||||
void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); }
|
||||
#else
|
||||
void setUsedRval() const {}
|
||||
void clearUsedRval() const {}
|
||||
void assertUnusedRval() const {}
|
||||
#endif
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS NoUsedRval
|
||||
{
|
||||
protected:
|
||||
void setUsedRval() const {}
|
||||
void clearUsedRval() const {}
|
||||
void assertUnusedRval() const {}
|
||||
};
|
||||
|
||||
template<class WantUsedRval>
|
||||
class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval
|
||||
{
|
||||
static_assert(mozilla::IsSame<WantUsedRval, IncludeUsedRval>::value ||
|
||||
mozilla::IsSame<WantUsedRval, NoUsedRval>::value,
|
||||
"WantUsedRval can only be IncludeUsedRval or NoUsedRval");
|
||||
|
||||
protected:
|
||||
Value* argv_;
|
||||
unsigned argc_;
|
||||
bool constructing_;
|
||||
|
||||
public:
|
||||
// CALLEE ACCESS
|
||||
|
||||
/*
|
||||
* Returns the function being called, as a value. Must not be called after
|
||||
* rval() has been used!
|
||||
*/
|
||||
HandleValue calleev() const {
|
||||
this->assertUnusedRval();
|
||||
return HandleValue::fromMarkedLocation(&argv_[-2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the function being called, as an object. Must not be called
|
||||
* after rval() has been used!
|
||||
*/
|
||||
JSObject& callee() const {
|
||||
return calleev().toObject();
|
||||
}
|
||||
|
||||
// CALLING/CONSTRUCTING-DIFFERENTIATIONS
|
||||
|
||||
bool isConstructing() const {
|
||||
if (!argv_[-1].isMagic())
|
||||
return false;
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
if (!this->usedRval_)
|
||||
CheckIsValidConstructible(calleev());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MutableHandleValue newTarget() const {
|
||||
MOZ_ASSERT(constructing_);
|
||||
return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the |this| value passed to the function. This method must not
|
||||
* be called when the function is being called as a constructor via |new|.
|
||||
* The value may or may not be an object: it is the individual function's
|
||||
* responsibility to box the value if needed.
|
||||
*/
|
||||
HandleValue thisv() const {
|
||||
// Some internal code uses thisv() in constructing cases, so don't do
|
||||
// this yet.
|
||||
// MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
|
||||
return HandleValue::fromMarkedLocation(&argv_[-1]);
|
||||
}
|
||||
|
||||
Value computeThis(JSContext* cx) const {
|
||||
if (thisv().isObject())
|
||||
return thisv();
|
||||
|
||||
return ComputeThis(cx, base());
|
||||
}
|
||||
|
||||
// ARGUMENTS
|
||||
|
||||
/* Returns the number of arguments. */
|
||||
unsigned length() const { return argc_; }
|
||||
|
||||
/* Returns the i-th zero-indexed argument. */
|
||||
MutableHandleValue operator[](unsigned i) const {
|
||||
MOZ_ASSERT(i < argc_);
|
||||
return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the i-th zero-indexed argument, or |undefined| if there's no
|
||||
* such argument.
|
||||
*/
|
||||
HandleValue get(unsigned i) const {
|
||||
return i < length()
|
||||
? HandleValue::fromMarkedLocation(&this->argv_[i])
|
||||
: UndefinedHandleValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the i-th zero-indexed argument is present and is not
|
||||
* |undefined|.
|
||||
*/
|
||||
bool hasDefined(unsigned i) const {
|
||||
return i < argc_ && !this->argv_[i].isUndefined();
|
||||
}
|
||||
|
||||
// RETURN VALUE
|
||||
|
||||
/*
|
||||
* Returns the currently-set return value. The initial contents of this
|
||||
* value are unspecified. Once this method has been called, callee() and
|
||||
* calleev() can no longer be used. (If you're compiling against a debug
|
||||
* build of SpiderMonkey, these methods will assert to aid debugging.)
|
||||
*
|
||||
* If the method you're implementing succeeds by returning true, you *must*
|
||||
* set this. (SpiderMonkey doesn't currently assert this, but it will do
|
||||
* so eventually.) You don't need to use or change this if your method
|
||||
* fails.
|
||||
*/
|
||||
MutableHandleValue rval() const {
|
||||
this->setUsedRval();
|
||||
return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
|
||||
}
|
||||
|
||||
public:
|
||||
// These methods are publicly exposed, but they are *not* to be used when
|
||||
// implementing a JSNative method and encapsulating access to |vp| within
|
||||
// it. You probably don't want to use these!
|
||||
|
||||
void setCallee(const Value& aCalleev) const {
|
||||
this->clearUsedRval();
|
||||
argv_[-2] = aCalleev;
|
||||
}
|
||||
|
||||
void setThis(const Value& aThisv) const {
|
||||
argv_[-1] = aThisv;
|
||||
}
|
||||
|
||||
MutableHandleValue mutableThisv() const {
|
||||
return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
|
||||
}
|
||||
|
||||
public:
|
||||
// These methods are publicly exposed, but we're unsure of the interfaces
|
||||
// (because they're hackish and drop assertions). Avoid using these if you
|
||||
// can.
|
||||
|
||||
Value* array() const { return argv_; }
|
||||
Value* end() const { return argv_ + argc_ + constructing_; }
|
||||
|
||||
public:
|
||||
// These methods are only intended for internal use. Embedders shouldn't
|
||||
// use them!
|
||||
|
||||
Value* base() const { return argv_ - 2; }
|
||||
|
||||
Value* spAfterCall() const {
|
||||
this->setUsedRval();
|
||||
return argv_ - 1;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval>
|
||||
{
|
||||
private:
|
||||
friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
|
||||
friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing);
|
||||
|
||||
static CallArgs create(unsigned argc, Value* argv, bool constructing) {
|
||||
CallArgs args;
|
||||
args.clearUsedRval();
|
||||
args.argv_ = argv;
|
||||
args.argc_ = argc;
|
||||
args.constructing_ = constructing;
|
||||
#ifdef DEBUG
|
||||
for (unsigned i = 0; i < argc; ++i)
|
||||
MOZ_ASSERT_IF(argv[i].isMarkable(), !GCThingIsMarkedGray(GCCellPtr(argv[i])));
|
||||
#endif
|
||||
return args;
|
||||
}
|
||||
|
||||
public:
|
||||
/*
|
||||
* Returns true if there are at least |required| arguments passed in. If
|
||||
* false, it reports an error message on the context.
|
||||
*/
|
||||
bool requireAtLeast(JSContext* cx, const char* fnname, unsigned required) const;
|
||||
|
||||
};
|
||||
|
||||
MOZ_ALWAYS_INLINE CallArgs
|
||||
CallArgsFromVp(unsigned argc, Value* vp)
|
||||
{
|
||||
return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING));
|
||||
}
|
||||
|
||||
// This method is only intended for internal use in SpiderMonkey. We may
|
||||
// eventually move it to an internal header. Embedders should use
|
||||
// JS::CallArgsFromVp!
|
||||
MOZ_ALWAYS_INLINE CallArgs
|
||||
CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false)
|
||||
{
|
||||
return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing);
|
||||
}
|
||||
|
||||
} // namespace JS
|
||||
|
||||
/*
|
||||
* Macros to hide interpreter stack layout details from a JSNative using its
|
||||
* JS::Value* vp parameter. DO NOT USE THESE! Instead use JS::CallArgs and
|
||||
* friends, above. These macros will be removed when we change JSNative to
|
||||
* take a const JS::CallArgs&.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Return |this| if |this| is an object. Otherwise, return the global object
|
||||
* if |this| is null or undefined, and finally return a boxed version of any
|
||||
* other primitive.
|
||||
*
|
||||
* Note: if this method returns null, an error has occurred and must be
|
||||
* propagated or caught.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE JS::Value
|
||||
JS_THIS(JSContext* cx, JS::Value* vp)
|
||||
{
|
||||
return vp[1].isPrimitive() ? JS::detail::ComputeThis(cx, vp) : vp[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* A note on JS_THIS_OBJECT: no equivalent method is part of the CallArgs
|
||||
* interface, and we're unlikely to add one (functions shouldn't be implicitly
|
||||
* exposing the global object to arbitrary callers). Continue using |vp|
|
||||
* directly for this case, but be aware this API will eventually be replaced
|
||||
* with a function that operates directly upon |args.thisv()|.
|
||||
*/
|
||||
#define JS_THIS_OBJECT(cx,vp) (JS_THIS(cx,vp).toObjectOrNull())
|
||||
|
||||
/*
|
||||
* |this| is passed to functions in ES5 without change. Functions themselves
|
||||
* do any post-processing they desire to box |this|, compute the global object,
|
||||
* &c. This macro retrieves a function's unboxed |this| value.
|
||||
*
|
||||
* This macro must not be used in conjunction with JS_THIS or JS_THIS_OBJECT,
|
||||
* or vice versa. Either use the provided this value with this macro, or
|
||||
* compute the boxed |this| value using those. JS_THIS_VALUE must not be used
|
||||
* if the function is being called as a constructor.
|
||||
*
|
||||
* But: DO NOT USE THIS! Instead use JS::CallArgs::thisv(), above.
|
||||
*
|
||||
*/
|
||||
#define JS_THIS_VALUE(cx,vp) ((vp)[1])
|
||||
|
||||
#endif /* js_CallArgs_h */
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_CallNonGenericMethod_h
|
||||
#define js_CallNonGenericMethod_h
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/CallArgs.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
// Returns true if |v| is considered an acceptable this-value.
|
||||
typedef bool (*IsAcceptableThis)(HandleValue v);
|
||||
|
||||
// Implements the guts of a method; guaranteed to be provided an acceptable
|
||||
// this-value, as determined by a corresponding IsAcceptableThis method.
|
||||
typedef bool (*NativeImpl)(JSContext* cx, const CallArgs& args);
|
||||
|
||||
namespace detail {
|
||||
|
||||
// DON'T CALL THIS DIRECTLY. It's for use only by CallNonGenericMethod!
|
||||
extern JS_PUBLIC_API(bool)
|
||||
CallMethodIfWrapped(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Methods usually act upon |this| objects only from a single global object and
|
||||
// compartment. Sometimes, however, a method must act upon |this| values from
|
||||
// multiple global objects or compartments. In such cases the |this| value a
|
||||
// method might see will be wrapped, such that various access to the object --
|
||||
// to its class, its private data, its reserved slots, and so on -- will not
|
||||
// work properly without entering that object's compartment. This method
|
||||
// implements a solution to this problem.
|
||||
//
|
||||
// To implement a method that accepts |this| values from multiple compartments,
|
||||
// define two functions. The first function matches the IsAcceptableThis type
|
||||
// and indicates whether the provided value is an acceptable |this| for the
|
||||
// method; it must be a pure function only of its argument.
|
||||
//
|
||||
// static const JSClass AnswerClass = { ... };
|
||||
//
|
||||
// static bool
|
||||
// IsAnswerObject(const Value& v)
|
||||
// {
|
||||
// if (!v.isObject())
|
||||
// return false;
|
||||
// return JS_GetClass(&v.toObject()) == &AnswerClass;
|
||||
// }
|
||||
//
|
||||
// The second function implements the NativeImpl signature and defines the
|
||||
// behavior of the method when it is provided an acceptable |this| value.
|
||||
// Aside from some typing niceties -- see the CallArgs interface for details --
|
||||
// its interface is the same as that of JSNative.
|
||||
//
|
||||
// static bool
|
||||
// answer_getAnswer_impl(JSContext* cx, JS::CallArgs args)
|
||||
// {
|
||||
// args.rval().setInt32(42);
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// The implementation function is guaranteed to be called *only* with a |this|
|
||||
// value which is considered acceptable.
|
||||
//
|
||||
// Now to implement the actual method, write a JSNative that calls the method
|
||||
// declared below, passing the appropriate template and runtime arguments.
|
||||
//
|
||||
// static bool
|
||||
// answer_getAnswer(JSContext* cx, unsigned argc, JS::Value* vp)
|
||||
// {
|
||||
// JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
// return JS::CallNonGenericMethod<IsAnswerObject, answer_getAnswer_impl>(cx, args);
|
||||
// }
|
||||
//
|
||||
// Note that, because they are used as template arguments, the predicate
|
||||
// and implementation functions must have external linkage. (This is
|
||||
// unfortunate, but GCC wasn't inlining things as one would hope when we
|
||||
// passed them as function arguments.)
|
||||
//
|
||||
// JS::CallNonGenericMethod will test whether |args.thisv()| is acceptable. If
|
||||
// it is, it will call the provided implementation function, which will return
|
||||
// a value and indicate success. If it is not, it will attempt to unwrap
|
||||
// |this| and call the implementation function on the unwrapped |this|. If
|
||||
// that succeeds, all well and good. If it doesn't succeed, a TypeError will
|
||||
// be thrown.
|
||||
//
|
||||
// Note: JS::CallNonGenericMethod will only work correctly if it's called in
|
||||
// tail position in a JSNative. Do not call it from any other place.
|
||||
//
|
||||
template<IsAcceptableThis Test, NativeImpl Impl>
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
CallNonGenericMethod(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
HandleValue thisv = args.thisv();
|
||||
if (Test(thisv))
|
||||
return Impl(cx, args);
|
||||
|
||||
return detail::CallMethodIfWrapped(cx, Test, Impl, args);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
CallNonGenericMethod(JSContext* cx, IsAcceptableThis Test, NativeImpl Impl, const CallArgs& args)
|
||||
{
|
||||
HandleValue thisv = args.thisv();
|
||||
if (Test(thisv))
|
||||
return Impl(cx, args);
|
||||
|
||||
return detail::CallMethodIfWrapped(cx, Test, Impl, args);
|
||||
}
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_CallNonGenericMethod_h */
|
||||
|
|
@ -1,338 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_CharacterEncoding_h
|
||||
#define js_CharacterEncoding_h
|
||||
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace js {
|
||||
class ExclusiveContext;
|
||||
} // namespace js
|
||||
|
||||
class JSFlatString;
|
||||
|
||||
namespace JS {
|
||||
|
||||
/*
|
||||
* By default, all C/C++ 1-byte-per-character strings passed into the JSAPI
|
||||
* are treated as ISO/IEC 8859-1, also known as Latin-1. That is, each
|
||||
* byte is treated as a 2-byte character, and there is no way to pass in a
|
||||
* string containing characters beyond U+00FF.
|
||||
*/
|
||||
class Latin1Chars : public mozilla::Range<Latin1Char>
|
||||
{
|
||||
typedef mozilla::Range<Latin1Char> Base;
|
||||
|
||||
public:
|
||||
using CharT = Latin1Char;
|
||||
|
||||
Latin1Chars() : Base() {}
|
||||
Latin1Chars(char* aBytes, size_t aLength) : Base(reinterpret_cast<Latin1Char*>(aBytes), aLength) {}
|
||||
Latin1Chars(const Latin1Char* aBytes, size_t aLength)
|
||||
: Base(const_cast<Latin1Char*>(aBytes), aLength)
|
||||
{}
|
||||
Latin1Chars(const char* aBytes, size_t aLength)
|
||||
: Base(reinterpret_cast<Latin1Char*>(const_cast<char*>(aBytes)), aLength)
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* A Latin1Chars, but with \0 termination for C compatibility.
|
||||
*/
|
||||
class Latin1CharsZ : public mozilla::RangedPtr<Latin1Char>
|
||||
{
|
||||
typedef mozilla::RangedPtr<Latin1Char> Base;
|
||||
|
||||
public:
|
||||
using CharT = Latin1Char;
|
||||
|
||||
Latin1CharsZ() : Base(nullptr, 0) {}
|
||||
|
||||
Latin1CharsZ(char* aBytes, size_t aLength)
|
||||
: Base(reinterpret_cast<Latin1Char*>(aBytes), aLength)
|
||||
{
|
||||
MOZ_ASSERT(aBytes[aLength] == '\0');
|
||||
}
|
||||
|
||||
Latin1CharsZ(Latin1Char* aBytes, size_t aLength)
|
||||
: Base(aBytes, aLength)
|
||||
{
|
||||
MOZ_ASSERT(aBytes[aLength] == '\0');
|
||||
}
|
||||
|
||||
using Base::operator=;
|
||||
|
||||
char* c_str() { return reinterpret_cast<char*>(get()); }
|
||||
};
|
||||
|
||||
class UTF8Chars : public mozilla::Range<unsigned char>
|
||||
{
|
||||
typedef mozilla::Range<unsigned char> Base;
|
||||
|
||||
public:
|
||||
using CharT = unsigned char;
|
||||
|
||||
UTF8Chars() : Base() {}
|
||||
UTF8Chars(char* aBytes, size_t aLength)
|
||||
: Base(reinterpret_cast<unsigned char*>(aBytes), aLength)
|
||||
{}
|
||||
UTF8Chars(const char* aBytes, size_t aLength)
|
||||
: Base(reinterpret_cast<unsigned char*>(const_cast<char*>(aBytes)), aLength)
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* SpiderMonkey also deals directly with UTF-8 encoded text in some places.
|
||||
*/
|
||||
class UTF8CharsZ : public mozilla::RangedPtr<unsigned char>
|
||||
{
|
||||
typedef mozilla::RangedPtr<unsigned char> Base;
|
||||
|
||||
public:
|
||||
using CharT = unsigned char;
|
||||
|
||||
UTF8CharsZ() : Base(nullptr, 0) {}
|
||||
|
||||
UTF8CharsZ(char* aBytes, size_t aLength)
|
||||
: Base(reinterpret_cast<unsigned char*>(aBytes), aLength)
|
||||
{
|
||||
MOZ_ASSERT(aBytes[aLength] == '\0');
|
||||
}
|
||||
|
||||
UTF8CharsZ(unsigned char* aBytes, size_t aLength)
|
||||
: Base(aBytes, aLength)
|
||||
{
|
||||
MOZ_ASSERT(aBytes[aLength] == '\0');
|
||||
}
|
||||
|
||||
using Base::operator=;
|
||||
|
||||
char* c_str() { return reinterpret_cast<char*>(get()); }
|
||||
};
|
||||
|
||||
/*
|
||||
* A wrapper for a "const char*" that is encoded using UTF-8.
|
||||
* This class does not manage ownership of the data; that is left
|
||||
* to others. This differs from UTF8CharsZ in that the chars are
|
||||
* const and it allows assignment.
|
||||
*/
|
||||
class ConstUTF8CharsZ
|
||||
{
|
||||
const char* data_;
|
||||
|
||||
public:
|
||||
using CharT = unsigned char;
|
||||
|
||||
ConstUTF8CharsZ() : data_(nullptr)
|
||||
{}
|
||||
|
||||
ConstUTF8CharsZ(const char* aBytes, size_t aLength)
|
||||
: data_(aBytes)
|
||||
{
|
||||
MOZ_ASSERT(aBytes[aLength] == '\0');
|
||||
#ifdef DEBUG
|
||||
validate(aLength);
|
||||
#endif
|
||||
}
|
||||
|
||||
const void* get() const { return data_; }
|
||||
|
||||
const char* c_str() const { return data_; }
|
||||
|
||||
explicit operator bool() const { return data_ != nullptr; }
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
void validate(size_t aLength);
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* SpiderMonkey uses a 2-byte character representation: it is a
|
||||
* 2-byte-at-a-time view of a UTF-16 byte stream. This is similar to UCS-2,
|
||||
* but unlike UCS-2, we do not strip UTF-16 extension bytes. This allows a
|
||||
* sufficiently dedicated JavaScript program to be fully unicode-aware by
|
||||
* manually interpreting UTF-16 extension characters embedded in the JS
|
||||
* string.
|
||||
*/
|
||||
class TwoByteChars : public mozilla::Range<char16_t>
|
||||
{
|
||||
typedef mozilla::Range<char16_t> Base;
|
||||
|
||||
public:
|
||||
using CharT = char16_t;
|
||||
|
||||
TwoByteChars() : Base() {}
|
||||
TwoByteChars(char16_t* aChars, size_t aLength) : Base(aChars, aLength) {}
|
||||
TwoByteChars(const char16_t* aChars, size_t aLength) : Base(const_cast<char16_t*>(aChars), aLength) {}
|
||||
};
|
||||
|
||||
/*
|
||||
* A TwoByteChars, but \0 terminated for compatibility with JSFlatString.
|
||||
*/
|
||||
class TwoByteCharsZ : public mozilla::RangedPtr<char16_t>
|
||||
{
|
||||
typedef mozilla::RangedPtr<char16_t> Base;
|
||||
|
||||
public:
|
||||
using CharT = char16_t;
|
||||
|
||||
TwoByteCharsZ() : Base(nullptr, 0) {}
|
||||
|
||||
TwoByteCharsZ(char16_t* chars, size_t length)
|
||||
: Base(chars, length)
|
||||
{
|
||||
MOZ_ASSERT(chars[length] == '\0');
|
||||
}
|
||||
|
||||
using Base::operator=;
|
||||
};
|
||||
|
||||
typedef mozilla::RangedPtr<const char16_t> ConstCharPtr;
|
||||
|
||||
/*
|
||||
* Like TwoByteChars, but the chars are const.
|
||||
*/
|
||||
class ConstTwoByteChars : public mozilla::Range<const char16_t>
|
||||
{
|
||||
typedef mozilla::Range<const char16_t> Base;
|
||||
|
||||
public:
|
||||
using CharT = char16_t;
|
||||
|
||||
ConstTwoByteChars() : Base() {}
|
||||
ConstTwoByteChars(const char16_t* aChars, size_t aLength) : Base(aChars, aLength) {}
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert a 2-byte character sequence to "ISO-Latin-1". This works by
|
||||
* truncating each 2-byte pair in the sequence to a 1-byte pair. If the source
|
||||
* contains any UTF-16 extension characters, then this may give invalid Latin1
|
||||
* output. The returned string is zero terminated. The returned string or the
|
||||
* returned string's |start()| must be freed with JS_free or js_free,
|
||||
* respectively. If allocation fails, an OOM error will be set and the method
|
||||
* will return a nullptr chars (which can be tested for with the ! operator).
|
||||
* This method cannot trigger GC.
|
||||
*/
|
||||
extern Latin1CharsZ
|
||||
LossyTwoByteCharsToNewLatin1CharsZ(js::ExclusiveContext* cx,
|
||||
const mozilla::Range<const char16_t> tbchars);
|
||||
|
||||
inline Latin1CharsZ
|
||||
LossyTwoByteCharsToNewLatin1CharsZ(js::ExclusiveContext* cx, const char16_t* begin, size_t length)
|
||||
{
|
||||
const mozilla::Range<const char16_t> tbchars(begin, length);
|
||||
return JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
extern UTF8CharsZ
|
||||
CharsToNewUTF8CharsZ(js::ExclusiveContext* maybeCx, const mozilla::Range<CharT> chars);
|
||||
|
||||
uint32_t
|
||||
Utf8ToOneUcs4Char(const uint8_t* utf8Buffer, int utf8Length);
|
||||
|
||||
/*
|
||||
* Inflate bytes in UTF-8 encoding to char16_t.
|
||||
* - On error, returns an empty TwoByteCharsZ.
|
||||
* - On success, returns a malloc'd TwoByteCharsZ, and updates |outlen| to hold
|
||||
* its length; the length value excludes the trailing null.
|
||||
*/
|
||||
extern TwoByteCharsZ
|
||||
UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen);
|
||||
|
||||
/*
|
||||
* Like UTF8CharsToNewTwoByteCharsZ, but for ConstUTF8CharsZ.
|
||||
*/
|
||||
extern TwoByteCharsZ
|
||||
UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen);
|
||||
|
||||
/*
|
||||
* The same as UTF8CharsToNewTwoByteCharsZ(), except that any malformed UTF-8 characters
|
||||
* will be replaced by \uFFFD. No exception will be thrown for malformed UTF-8
|
||||
* input.
|
||||
*/
|
||||
extern TwoByteCharsZ
|
||||
LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen);
|
||||
|
||||
extern TwoByteCharsZ
|
||||
LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen);
|
||||
|
||||
/*
|
||||
* Returns the length of the char buffer required to encode |s| as UTF8.
|
||||
* Does not include the null-terminator.
|
||||
*/
|
||||
JS_PUBLIC_API(size_t)
|
||||
GetDeflatedUTF8StringLength(JSFlatString* s);
|
||||
|
||||
/*
|
||||
* Encode |src| as UTF8. The caller must either ensure |dst| has enough space
|
||||
* to encode the entire string or pass the length of the buffer as |dstlenp|,
|
||||
* in which case the function will encode characters from the string until
|
||||
* the buffer is exhausted. Does not write the null terminator.
|
||||
*
|
||||
* If |dstlenp| is provided, it will be updated to hold the number of bytes
|
||||
* written to the buffer. If |numcharsp| is provided, it will be updated to hold
|
||||
* the number of Unicode characters written to the buffer (which can be less
|
||||
* than the length of the string, if the buffer is exhausted before the string
|
||||
* is fully encoded).
|
||||
*/
|
||||
JS_PUBLIC_API(void)
|
||||
DeflateStringToUTF8Buffer(JSFlatString* src, mozilla::RangedPtr<char> dst,
|
||||
size_t* dstlenp = nullptr, size_t* numcharsp = nullptr);
|
||||
|
||||
/*
|
||||
* The smallest character encoding capable of fully representing a particular
|
||||
* string.
|
||||
*/
|
||||
enum class SmallestEncoding {
|
||||
ASCII,
|
||||
Latin1,
|
||||
UTF16
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the smallest encoding possible for the given string: if all
|
||||
* codepoints are <128 then ASCII, otherwise if all codepoints are <256
|
||||
* Latin-1, else UTF16.
|
||||
*/
|
||||
JS_PUBLIC_API(SmallestEncoding)
|
||||
FindSmallestEncoding(UTF8Chars utf8);
|
||||
|
||||
/*
|
||||
* Return a null-terminated Latin-1 string copied from the input string,
|
||||
* storing its length (excluding null terminator) in |*outlen|. Fail and
|
||||
* report an error if the string contains non-Latin-1 codepoints. Returns
|
||||
* Latin1CharsZ() on failure.
|
||||
*/
|
||||
extern Latin1CharsZ
|
||||
UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen);
|
||||
|
||||
/*
|
||||
* Return a null-terminated Latin-1 string copied from the input string,
|
||||
* storing its length (excluding null terminator) in |*outlen|. Non-Latin-1
|
||||
* codepoints are replaced by '?'. Returns Latin1CharsZ() on failure.
|
||||
*/
|
||||
extern Latin1CharsZ
|
||||
LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen);
|
||||
|
||||
/*
|
||||
* Returns true if all characters in the given null-terminated string are
|
||||
* ASCII, i.e. < 0x80, false otherwise.
|
||||
*/
|
||||
extern bool
|
||||
StringIsASCII(const char* s);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
inline void JS_free(JS::Latin1CharsZ& ptr) { js_free((void*)ptr.get()); }
|
||||
inline void JS_free(JS::UTF8CharsZ& ptr) { js_free((void*)ptr.get()); }
|
||||
|
||||
#endif /* js_CharacterEncoding_h */
|
||||
|
|
@ -1,995 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* JSClass definition and its component types, plus related interfaces. */
|
||||
|
||||
#ifndef js_Class_h
|
||||
#define js_Class_h
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/CallArgs.h"
|
||||
#include "js/Id.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
/*
|
||||
* A JSClass acts as a vtable for JS objects that allows JSAPI clients to
|
||||
* control various aspects of the behavior of an object like property lookup.
|
||||
* js::Class is an engine-private extension that allows more control over
|
||||
* object behavior and, e.g., allows custom slow layout.
|
||||
*/
|
||||
|
||||
struct JSAtomState;
|
||||
struct JSFreeOp;
|
||||
struct JSFunctionSpec;
|
||||
|
||||
namespace js {
|
||||
|
||||
struct Class;
|
||||
class FreeOp;
|
||||
class Shape;
|
||||
|
||||
// This is equal to JSFunction::class_. Use it in places where you don't want
|
||||
// to #include jsfun.h.
|
||||
extern JS_FRIEND_DATA(const js::Class* const) FunctionClassPtr;
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
class AutoIdVector;
|
||||
|
||||
/**
|
||||
* The answer to a successful query as to whether an object is an Array per
|
||||
* ES6's internal |IsArray| operation (as exposed by |Array.isArray|).
|
||||
*/
|
||||
enum class IsArrayAnswer
|
||||
{
|
||||
Array,
|
||||
NotArray,
|
||||
RevokedProxy
|
||||
};
|
||||
|
||||
/**
|
||||
* ES6 7.2.2.
|
||||
*
|
||||
* Returns false on failure, otherwise returns true and sets |*isArray|
|
||||
* indicating whether the object passes ECMAScript's IsArray test. This is the
|
||||
* same test performed by |Array.isArray|.
|
||||
*
|
||||
* This is NOT the same as asking whether |obj| is an Array or a wrapper around
|
||||
* one. If |obj| is a proxy created by |Proxy.revocable()| and has been
|
||||
* revoked, or if |obj| is a proxy whose target (at any number of hops) is a
|
||||
* revoked proxy, this method throws a TypeError and returns false.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsArray(JSContext* cx, HandleObject obj, bool* isArray);
|
||||
|
||||
/**
|
||||
* Identical to IsArray above, but the nature of the object (if successfully
|
||||
* determined) is communicated via |*answer|. In particular this method
|
||||
* returns true and sets |*answer = IsArrayAnswer::RevokedProxy| when called on
|
||||
* a revoked proxy.
|
||||
*
|
||||
* Most users will want the overload above, not this one.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer);
|
||||
|
||||
/**
|
||||
* Per ES6, the [[DefineOwnProperty]] internal method has three different
|
||||
* possible outcomes:
|
||||
*
|
||||
* - It can throw an exception (which we indicate by returning false).
|
||||
*
|
||||
* - It can return true, indicating unvarnished success.
|
||||
*
|
||||
* - It can return false, indicating "strict failure". The property could
|
||||
* not be defined. It's an error, but no exception was thrown.
|
||||
*
|
||||
* It's not just [[DefineOwnProperty]]: all the mutating internal methods have
|
||||
* the same three outcomes. (The other affected internal methods are [[Set]],
|
||||
* [[Delete]], [[SetPrototypeOf]], and [[PreventExtensions]].)
|
||||
*
|
||||
* If you think this design is awful, you're not alone. But as it's the
|
||||
* standard, we must represent these boolean "success" values somehow.
|
||||
* ObjectOpSuccess is the class for this. It's like a bool, but when it's false
|
||||
* it also stores an error code.
|
||||
*
|
||||
* Typical usage:
|
||||
*
|
||||
* ObjectOpResult result;
|
||||
* if (!DefineProperty(cx, obj, id, ..., result))
|
||||
* return false;
|
||||
* if (!result)
|
||||
* return result.reportError(cx, obj, id);
|
||||
*
|
||||
* Users don't have to call `result.report()`; another possible ending is:
|
||||
*
|
||||
* argv.rval().setBoolean(bool(result));
|
||||
* return true;
|
||||
*/
|
||||
class ObjectOpResult
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* code_ is either one of the special codes OkCode or Uninitialized, or
|
||||
* an error code. For now the error codes are private to the JS engine;
|
||||
* they're defined in js/src/js.msg.
|
||||
*
|
||||
* code_ is uintptr_t (rather than uint32_t) for the convenience of the
|
||||
* JITs, which would otherwise have to deal with either padding or stack
|
||||
* alignment on 64-bit platforms.
|
||||
*/
|
||||
uintptr_t code_;
|
||||
|
||||
public:
|
||||
enum SpecialCodes : uintptr_t {
|
||||
OkCode = 0,
|
||||
Uninitialized = uintptr_t(-1)
|
||||
};
|
||||
|
||||
ObjectOpResult() : code_(Uninitialized) {}
|
||||
|
||||
/* Return true if succeed() was called. */
|
||||
bool ok() const {
|
||||
MOZ_ASSERT(code_ != Uninitialized);
|
||||
return code_ == OkCode;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return ok(); }
|
||||
|
||||
/* Set this ObjectOpResult to true and return true. */
|
||||
bool succeed() {
|
||||
code_ = OkCode;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set this ObjectOpResult to false with an error code.
|
||||
*
|
||||
* Always returns true, as a convenience. Typical usage will be:
|
||||
*
|
||||
* if (funny condition)
|
||||
* return result.fail(JSMSG_CANT_DO_THE_THINGS);
|
||||
*
|
||||
* The true return value indicates that no exception is pending, and it
|
||||
* would be OK to ignore the failure and continue.
|
||||
*/
|
||||
bool fail(uint32_t msg) {
|
||||
MOZ_ASSERT(msg != OkCode);
|
||||
code_ = msg;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool) failCantRedefineProp();
|
||||
JS_PUBLIC_API(bool) failReadOnly();
|
||||
JS_PUBLIC_API(bool) failGetterOnly();
|
||||
JS_PUBLIC_API(bool) failCantDelete();
|
||||
|
||||
JS_PUBLIC_API(bool) failCantSetInterposed();
|
||||
JS_PUBLIC_API(bool) failCantDefineWindowElement();
|
||||
JS_PUBLIC_API(bool) failCantDeleteWindowElement();
|
||||
JS_PUBLIC_API(bool) failCantDeleteWindowNamedProperty();
|
||||
JS_PUBLIC_API(bool) failCantPreventExtensions();
|
||||
JS_PUBLIC_API(bool) failCantSetProto();
|
||||
JS_PUBLIC_API(bool) failNoNamedSetter();
|
||||
JS_PUBLIC_API(bool) failNoIndexedSetter();
|
||||
|
||||
uint32_t failureCode() const {
|
||||
MOZ_ASSERT(!ok());
|
||||
return uint32_t(code_);
|
||||
}
|
||||
|
||||
/*
|
||||
* Report an error or warning if necessary; return true to proceed and
|
||||
* false if an error was reported. Call this when failure should cause
|
||||
* a warning if extraWarnings are enabled.
|
||||
*
|
||||
* The precise rules are like this:
|
||||
*
|
||||
* - If ok(), then we succeeded. Do nothing and return true.
|
||||
* - Otherwise, if |strict| is true, or if cx has both extraWarnings and
|
||||
* werrorOption enabled, throw a TypeError and return false.
|
||||
* - Otherwise, if cx has extraWarnings enabled, emit a warning and
|
||||
* return true.
|
||||
* - Otherwise, do nothing and return true.
|
||||
*/
|
||||
bool checkStrictErrorOrWarning(JSContext* cx, HandleObject obj, HandleId id, bool strict) {
|
||||
if (ok())
|
||||
return true;
|
||||
return reportStrictErrorOrWarning(cx, obj, id, strict);
|
||||
}
|
||||
|
||||
/*
|
||||
* The same as checkStrictErrorOrWarning(cx, id, strict), except the
|
||||
* operation is not associated with a particular property id. This is
|
||||
* used for [[PreventExtensions]] and [[SetPrototypeOf]]. failureCode()
|
||||
* must not be an error that has "{0}" in the error message.
|
||||
*/
|
||||
bool checkStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict) {
|
||||
return ok() || reportStrictErrorOrWarning(cx, obj, strict);
|
||||
}
|
||||
|
||||
/* Throw a TypeError. Call this only if !ok(). */
|
||||
bool reportError(JSContext* cx, HandleObject obj, HandleId id) {
|
||||
return reportStrictErrorOrWarning(cx, obj, id, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* The same as reportError(cx, obj, id), except the operation is not
|
||||
* associated with a particular property id.
|
||||
*/
|
||||
bool reportError(JSContext* cx, HandleObject obj) {
|
||||
return reportStrictErrorOrWarning(cx, obj, true);
|
||||
}
|
||||
|
||||
/* Helper function for checkStrictErrorOrWarning's slow path. */
|
||||
JS_PUBLIC_API(bool) reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, HandleId id, bool strict);
|
||||
JS_PUBLIC_API(bool) reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict);
|
||||
|
||||
/*
|
||||
* Convenience method. Return true if ok() or if strict is false; otherwise
|
||||
* throw a TypeError and return false.
|
||||
*/
|
||||
bool checkStrict(JSContext* cx, HandleObject obj, HandleId id) {
|
||||
return checkStrictErrorOrWarning(cx, obj, id, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience method. The same as checkStrict(cx, id), except the
|
||||
* operation is not associated with a particular property id.
|
||||
*/
|
||||
bool checkStrict(JSContext* cx, HandleObject obj) {
|
||||
return checkStrictErrorOrWarning(cx, obj, true);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
// JSClass operation signatures.
|
||||
|
||||
/**
|
||||
* Get a property named by id in obj. Note the jsid id type -- id may
|
||||
* be a string (Unicode property identifier) or an int (element index). The
|
||||
* *vp out parameter, on success, is the new property value after the action.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSGetterOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||
JS::MutableHandleValue vp);
|
||||
|
||||
/** Add a property named by id to obj. */
|
||||
typedef bool
|
||||
(* JSAddPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v);
|
||||
|
||||
/**
|
||||
* Set a property named by id in obj, treating the assignment as strict
|
||||
* mode code if strict is true. Note the jsid id type -- id may be a string
|
||||
* (Unicode property identifier) or an int (element index). The *vp out
|
||||
* parameter, on success, is the new property value after the
|
||||
* set.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSSetterOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||
JS::MutableHandleValue vp, JS::ObjectOpResult& result);
|
||||
|
||||
/**
|
||||
* Delete a property named by id in obj.
|
||||
*
|
||||
* If an error occurred, return false as per normal JSAPI error practice.
|
||||
*
|
||||
* If no error occurred, but the deletion attempt wasn't allowed (perhaps
|
||||
* because the property was non-configurable), call result.fail() and
|
||||
* return true. This will cause |delete obj[id]| to evaluate to false in
|
||||
* non-strict mode code, and to throw a TypeError in strict mode code.
|
||||
*
|
||||
* If no error occurred and the deletion wasn't disallowed (this is *not* the
|
||||
* same as saying that a deletion actually occurred -- deleting a non-existent
|
||||
* property, or an inherited property, is allowed -- it's just pointless),
|
||||
* call result.succeed() and return true.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSDeletePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||
JS::ObjectOpResult& result);
|
||||
|
||||
/**
|
||||
* The type of ObjectOps::enumerate. This callback overrides a portion of
|
||||
* SpiderMonkey's default [[Enumerate]] internal method. When an ordinary object
|
||||
* is enumerated, that object and each object on its prototype chain is tested
|
||||
* for an enumerate op, and those ops are called in order. The properties each
|
||||
* op adds to the 'properties' vector are added to the set of values the for-in
|
||||
* loop will iterate over. All of this is nonstandard.
|
||||
*
|
||||
* An object is "enumerated" when it's the target of a for-in loop or
|
||||
* JS_Enumerate(). The callback's job is to populate 'properties' with the
|
||||
* object's property keys. If `enumerableOnly` is true, the callback should only
|
||||
* add enumerable properties.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSNewEnumerateOp)(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties,
|
||||
bool enumerableOnly);
|
||||
|
||||
/**
|
||||
* The old-style JSClass.enumerate op should define all lazy properties not
|
||||
* yet reflected in obj.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSEnumerateOp)(JSContext* cx, JS::HandleObject obj);
|
||||
|
||||
/**
|
||||
* The type of ObjectOps::funToString. This callback allows an object to
|
||||
* provide a custom string to use when Function.prototype.toString is invoked on
|
||||
* that object. A null return value means OOM.
|
||||
*/
|
||||
typedef JSString*
|
||||
(* JSFunToStringOp)(JSContext* cx, JS::HandleObject obj, unsigned indent);
|
||||
|
||||
/**
|
||||
* Resolve a lazy property named by id in obj by defining it directly in obj.
|
||||
* Lazy properties are those reflected from some peer native property space
|
||||
* (e.g., the DOM attributes for a given node reflected as obj) on demand.
|
||||
*
|
||||
* JS looks for a property in an object, and if not found, tries to resolve
|
||||
* the given id. *resolvedp should be set to true iff the property was defined
|
||||
* on |obj|.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSResolveOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||
bool* resolvedp);
|
||||
|
||||
/**
|
||||
* A class with a resolve hook can optionally have a mayResolve hook. This hook
|
||||
* must have no side effects and must return true for a given id if the resolve
|
||||
* hook may resolve this id. This is useful when we're doing a "pure" lookup: if
|
||||
* mayResolve returns false, we know we don't have to call the effectful resolve
|
||||
* hook.
|
||||
*
|
||||
* maybeObj, if non-null, is the object on which we're doing the lookup. This
|
||||
* can be nullptr: during JIT compilation we sometimes know the Class but not
|
||||
* the object.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSMayResolveOp)(const JSAtomState& names, jsid id, JSObject* maybeObj);
|
||||
|
||||
/**
|
||||
* Finalize obj, which the garbage collector has determined to be unreachable
|
||||
* from other live objects or from GC roots. Obviously, finalizers must never
|
||||
* store a reference to obj.
|
||||
*/
|
||||
typedef void
|
||||
(* JSFinalizeOp)(JSFreeOp* fop, JSObject* obj);
|
||||
|
||||
/** Finalizes external strings created by JS_NewExternalString. */
|
||||
struct JSStringFinalizer {
|
||||
void (*finalize)(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether v is an instance of obj. Return false on error or exception,
|
||||
* true on success with true in *bp if v is an instance of obj, false in
|
||||
* *bp otherwise.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSHasInstanceOp)(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp,
|
||||
bool* bp);
|
||||
|
||||
/**
|
||||
* Function type for trace operation of the class called to enumerate all
|
||||
* traceable things reachable from obj's private data structure. For each such
|
||||
* thing, a trace implementation must call JS::TraceEdge on the thing's
|
||||
* location.
|
||||
*
|
||||
* JSTraceOp implementation can assume that no other threads mutates object
|
||||
* state. It must not change state of the object or corresponding native
|
||||
* structures. The only exception for this rule is the case when the embedding
|
||||
* needs a tight integration with GC. In that case the embedding can check if
|
||||
* the traversal is a part of the marking phase through calling
|
||||
* JS_IsGCMarkingTracer and apply a special code like emptying caches or
|
||||
* marking its native structures.
|
||||
*/
|
||||
typedef void
|
||||
(* JSTraceOp)(JSTracer* trc, JSObject* obj);
|
||||
|
||||
typedef JSObject*
|
||||
(* JSWeakmapKeyDelegateOp)(JSObject* obj);
|
||||
|
||||
typedef void
|
||||
(* JSObjectMovedOp)(JSObject* obj, const JSObject* old);
|
||||
|
||||
/* js::Class operation signatures. */
|
||||
|
||||
namespace js {
|
||||
|
||||
typedef bool
|
||||
(* LookupPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||
JS::MutableHandleObject objp, JS::MutableHandle<Shape*> propp);
|
||||
typedef bool
|
||||
(* DefinePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::ObjectOpResult& result);
|
||||
typedef bool
|
||||
(* HasPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
|
||||
typedef bool
|
||||
(* GetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleValue receiver, JS::HandleId id,
|
||||
JS::MutableHandleValue vp);
|
||||
typedef bool
|
||||
(* SetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v,
|
||||
JS::HandleValue receiver, JS::ObjectOpResult& result);
|
||||
typedef bool
|
||||
(* GetOwnPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc);
|
||||
typedef bool
|
||||
(* DeletePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||
JS::ObjectOpResult& result);
|
||||
|
||||
typedef bool
|
||||
(* WatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
|
||||
|
||||
typedef bool
|
||||
(* UnwatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id);
|
||||
|
||||
class JS_FRIEND_API(ElementAdder)
|
||||
{
|
||||
public:
|
||||
enum GetBehavior {
|
||||
// Check if the element exists before performing the Get and preserve
|
||||
// holes.
|
||||
CheckHasElemPreserveHoles,
|
||||
|
||||
// Perform a Get operation, like obj[index] in JS.
|
||||
GetElement
|
||||
};
|
||||
|
||||
private:
|
||||
// Only one of these is used.
|
||||
JS::RootedObject resObj_;
|
||||
JS::Value* vp_;
|
||||
|
||||
uint32_t index_;
|
||||
#ifdef DEBUG
|
||||
uint32_t length_;
|
||||
#endif
|
||||
GetBehavior getBehavior_;
|
||||
|
||||
public:
|
||||
ElementAdder(JSContext* cx, JSObject* obj, uint32_t length, GetBehavior behavior)
|
||||
: resObj_(cx, obj), vp_(nullptr), index_(0),
|
||||
#ifdef DEBUG
|
||||
length_(length),
|
||||
#endif
|
||||
getBehavior_(behavior)
|
||||
{}
|
||||
ElementAdder(JSContext* cx, JS::Value* vp, uint32_t length, GetBehavior behavior)
|
||||
: resObj_(cx), vp_(vp), index_(0),
|
||||
#ifdef DEBUG
|
||||
length_(length),
|
||||
#endif
|
||||
getBehavior_(behavior)
|
||||
{}
|
||||
|
||||
GetBehavior getBehavior() const { return getBehavior_; }
|
||||
|
||||
bool append(JSContext* cx, JS::HandleValue v);
|
||||
void appendHole();
|
||||
};
|
||||
|
||||
typedef bool
|
||||
(* GetElementsOp)(JSContext* cx, JS::HandleObject obj, uint32_t begin, uint32_t end,
|
||||
ElementAdder* adder);
|
||||
|
||||
typedef void
|
||||
(* FinalizeOp)(FreeOp* fop, JSObject* obj);
|
||||
|
||||
// The special treatment of |finalize| and |trace| is necessary because if we
|
||||
// assign either of those hooks to a local variable and then call it -- as is
|
||||
// done with the other hooks -- the GC hazard analysis gets confused.
|
||||
#define JS_CLASS_MEMBERS(ClassOpsType, FreeOpType) \
|
||||
const char* name; \
|
||||
uint32_t flags; \
|
||||
const ClassOpsType* cOps; \
|
||||
\
|
||||
JSAddPropertyOp getAddProperty() const { return cOps ? cOps->addProperty : nullptr; } \
|
||||
JSDeletePropertyOp getDelProperty() const { return cOps ? cOps->delProperty : nullptr; } \
|
||||
JSGetterOp getGetProperty() const { return cOps ? cOps->getProperty : nullptr; } \
|
||||
JSSetterOp getSetProperty() const { return cOps ? cOps->setProperty : nullptr; } \
|
||||
JSEnumerateOp getEnumerate() const { return cOps ? cOps->enumerate : nullptr; } \
|
||||
JSResolveOp getResolve() const { return cOps ? cOps->resolve : nullptr; } \
|
||||
JSMayResolveOp getMayResolve() const { return cOps ? cOps->mayResolve : nullptr; } \
|
||||
JSNative getCall() const { return cOps ? cOps->call : nullptr; } \
|
||||
JSHasInstanceOp getHasInstance() const { return cOps ? cOps->hasInstance : nullptr; } \
|
||||
JSNative getConstruct() const { return cOps ? cOps->construct : nullptr; } \
|
||||
\
|
||||
bool hasFinalize() const { return cOps && cOps->finalize; } \
|
||||
bool hasTrace() const { return cOps && cOps->trace; } \
|
||||
\
|
||||
bool isTrace(JSTraceOp trace) const { return cOps && cOps->trace == trace; } \
|
||||
\
|
||||
void doFinalize(FreeOpType* fop, JSObject* obj) const { \
|
||||
MOZ_ASSERT(cOps && cOps->finalize); \
|
||||
cOps->finalize(fop, obj); \
|
||||
} \
|
||||
void doTrace(JSTracer* trc, JSObject* obj) const { \
|
||||
MOZ_ASSERT(cOps && cOps->trace); \
|
||||
cOps->trace(trc, obj); \
|
||||
}
|
||||
|
||||
struct ClassOps
|
||||
{
|
||||
/* Function pointer members (may be null). */
|
||||
JSAddPropertyOp addProperty;
|
||||
JSDeletePropertyOp delProperty;
|
||||
JSGetterOp getProperty;
|
||||
JSSetterOp setProperty;
|
||||
JSEnumerateOp enumerate;
|
||||
JSResolveOp resolve;
|
||||
JSMayResolveOp mayResolve;
|
||||
FinalizeOp finalize;
|
||||
JSNative call;
|
||||
JSHasInstanceOp hasInstance;
|
||||
JSNative construct;
|
||||
JSTraceOp trace;
|
||||
};
|
||||
|
||||
/** Callback for the creation of constructor and prototype objects. */
|
||||
typedef JSObject* (*ClassObjectCreationOp)(JSContext* cx, JSProtoKey key);
|
||||
|
||||
/** Callback for custom post-processing after class initialization via ClassSpec. */
|
||||
typedef bool (*FinishClassInitOp)(JSContext* cx, JS::HandleObject ctor,
|
||||
JS::HandleObject proto);
|
||||
|
||||
const size_t JSCLASS_CACHED_PROTO_WIDTH = 6;
|
||||
|
||||
struct ClassSpec
|
||||
{
|
||||
// All properties except flags should be accessed through accessor.
|
||||
ClassObjectCreationOp createConstructor_;
|
||||
ClassObjectCreationOp createPrototype_;
|
||||
const JSFunctionSpec* constructorFunctions_;
|
||||
const JSPropertySpec* constructorProperties_;
|
||||
const JSFunctionSpec* prototypeFunctions_;
|
||||
const JSPropertySpec* prototypeProperties_;
|
||||
FinishClassInitOp finishInit_;
|
||||
uintptr_t flags;
|
||||
|
||||
static const size_t ProtoKeyWidth = JSCLASS_CACHED_PROTO_WIDTH;
|
||||
|
||||
static const uintptr_t ProtoKeyMask = (1 << ProtoKeyWidth) - 1;
|
||||
static const uintptr_t DontDefineConstructor = 1 << ProtoKeyWidth;
|
||||
static const uintptr_t IsDelegated = 1 << (ProtoKeyWidth + 1);
|
||||
|
||||
bool defined() const { return !!createConstructor_; }
|
||||
|
||||
bool delegated() const {
|
||||
return (flags & IsDelegated);
|
||||
}
|
||||
|
||||
// The ProtoKey this class inherits from.
|
||||
JSProtoKey inheritanceProtoKey() const {
|
||||
MOZ_ASSERT(defined());
|
||||
static_assert(JSProto_Null == 0, "zeroed key must be null");
|
||||
|
||||
// Default: Inherit from Object.
|
||||
if (!(flags & ProtoKeyMask))
|
||||
return JSProto_Object;
|
||||
|
||||
return JSProtoKey(flags & ProtoKeyMask);
|
||||
}
|
||||
|
||||
bool shouldDefineConstructor() const {
|
||||
MOZ_ASSERT(defined());
|
||||
return !(flags & DontDefineConstructor);
|
||||
}
|
||||
|
||||
const ClassSpec* delegatedClassSpec() const {
|
||||
MOZ_ASSERT(delegated());
|
||||
return reinterpret_cast<ClassSpec*>(createConstructor_);
|
||||
}
|
||||
|
||||
ClassObjectCreationOp createConstructorHook() const {
|
||||
if (delegated())
|
||||
return delegatedClassSpec()->createConstructorHook();
|
||||
return createConstructor_;
|
||||
}
|
||||
ClassObjectCreationOp createPrototypeHook() const {
|
||||
if (delegated())
|
||||
return delegatedClassSpec()->createPrototypeHook();
|
||||
return createPrototype_;
|
||||
}
|
||||
const JSFunctionSpec* constructorFunctions() const {
|
||||
if (delegated())
|
||||
return delegatedClassSpec()->constructorFunctions();
|
||||
return constructorFunctions_;
|
||||
}
|
||||
const JSPropertySpec* constructorProperties() const {
|
||||
if (delegated())
|
||||
return delegatedClassSpec()->constructorProperties();
|
||||
return constructorProperties_;
|
||||
}
|
||||
const JSFunctionSpec* prototypeFunctions() const {
|
||||
if (delegated())
|
||||
return delegatedClassSpec()->prototypeFunctions();
|
||||
return prototypeFunctions_;
|
||||
}
|
||||
const JSPropertySpec* prototypeProperties() const {
|
||||
if (delegated())
|
||||
return delegatedClassSpec()->prototypeProperties();
|
||||
return prototypeProperties_;
|
||||
}
|
||||
FinishClassInitOp finishInitHook() const {
|
||||
if (delegated())
|
||||
return delegatedClassSpec()->finishInitHook();
|
||||
return finishInit_;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClassExtension
|
||||
{
|
||||
/**
|
||||
* If an object is used as a key in a weakmap, it may be desirable for the
|
||||
* garbage collector to keep that object around longer than it otherwise
|
||||
* would. A common case is when the key is a wrapper around an object in
|
||||
* another compartment, and we want to avoid collecting the wrapper (and
|
||||
* removing the weakmap entry) as long as the wrapped object is alive. In
|
||||
* that case, the wrapped object is returned by the wrapper's
|
||||
* weakmapKeyDelegateOp hook. As long as the wrapper is used as a weakmap
|
||||
* key, it will not be collected (and remain in the weakmap) until the
|
||||
* wrapped object is collected.
|
||||
*/
|
||||
JSWeakmapKeyDelegateOp weakmapKeyDelegateOp;
|
||||
|
||||
/**
|
||||
* Optional hook called when an object is moved by a compacting GC.
|
||||
*
|
||||
* There may exist weak pointers to an object that are not traced through
|
||||
* when the normal trace APIs are used, for example objects in the wrapper
|
||||
* cache. This hook allows these pointers to be updated.
|
||||
*
|
||||
* Note that this hook can be called before JS_NewObject() returns if a GC
|
||||
* is triggered during construction of the object. This can happen for
|
||||
* global objects for example.
|
||||
*/
|
||||
JSObjectMovedOp objectMovedOp;
|
||||
};
|
||||
|
||||
inline ClassObjectCreationOp DELEGATED_CLASSSPEC(const ClassSpec* spec) {
|
||||
return reinterpret_cast<ClassObjectCreationOp>(const_cast<ClassSpec*>(spec));
|
||||
}
|
||||
|
||||
#define JS_NULL_CLASS_SPEC nullptr
|
||||
#define JS_NULL_CLASS_EXT nullptr
|
||||
|
||||
struct ObjectOps
|
||||
{
|
||||
LookupPropertyOp lookupProperty;
|
||||
DefinePropertyOp defineProperty;
|
||||
HasPropertyOp hasProperty;
|
||||
GetPropertyOp getProperty;
|
||||
SetPropertyOp setProperty;
|
||||
GetOwnPropertyOp getOwnPropertyDescriptor;
|
||||
DeletePropertyOp deleteProperty;
|
||||
WatchOp watch;
|
||||
UnwatchOp unwatch;
|
||||
GetElementsOp getElements;
|
||||
JSNewEnumerateOp enumerate;
|
||||
JSFunToStringOp funToString;
|
||||
};
|
||||
|
||||
#define JS_NULL_OBJECT_OPS nullptr
|
||||
|
||||
} // namespace js
|
||||
|
||||
// Classes, objects, and properties.
|
||||
|
||||
typedef void (*JSClassInternal)();
|
||||
|
||||
struct JSClassOps
|
||||
{
|
||||
/* Function pointer members (may be null). */
|
||||
JSAddPropertyOp addProperty;
|
||||
JSDeletePropertyOp delProperty;
|
||||
JSGetterOp getProperty;
|
||||
JSSetterOp setProperty;
|
||||
JSEnumerateOp enumerate;
|
||||
JSResolveOp resolve;
|
||||
JSMayResolveOp mayResolve;
|
||||
JSFinalizeOp finalize;
|
||||
JSNative call;
|
||||
JSHasInstanceOp hasInstance;
|
||||
JSNative construct;
|
||||
JSTraceOp trace;
|
||||
};
|
||||
|
||||
#define JS_NULL_CLASS_OPS nullptr
|
||||
|
||||
struct JSClass {
|
||||
JS_CLASS_MEMBERS(JSClassOps, JSFreeOp);
|
||||
|
||||
void* reserved[3];
|
||||
};
|
||||
|
||||
#define JSCLASS_HAS_PRIVATE (1<<0) // objects have private slot
|
||||
#define JSCLASS_DELAY_METADATA_BUILDER (1<<1) // class's initialization code
|
||||
// will call
|
||||
// SetNewObjectMetadata itself
|
||||
#define JSCLASS_IS_WRAPPED_NATIVE (1<<2) // class is an XPCWrappedNative.
|
||||
// WeakMaps use this to override
|
||||
// the wrapper disposal
|
||||
// mechanism.
|
||||
#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) // private is (nsISupports*)
|
||||
#define JSCLASS_IS_DOMJSCLASS (1<<4) // objects are DOM
|
||||
#define JSCLASS_HAS_XRAYED_CONSTRUCTOR (1<<5) // if wrapped by an xray
|
||||
// wrapper, the builtin
|
||||
// class's constructor won't
|
||||
// be unwrapped and invoked.
|
||||
// Instead, the constructor is
|
||||
// resolved in the caller's
|
||||
// compartment and invoked
|
||||
// with a wrapped newTarget.
|
||||
// The constructor has to
|
||||
// detect and handle this
|
||||
// situation.
|
||||
// See PromiseConstructor for
|
||||
// details.
|
||||
#define JSCLASS_EMULATES_UNDEFINED (1<<6) // objects of this class act
|
||||
// like the value undefined,
|
||||
// in some contexts
|
||||
#define JSCLASS_USERBIT1 (1<<7) // Reserved for embeddings.
|
||||
|
||||
// To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or
|
||||
// JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where
|
||||
// n is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1.
|
||||
#define JSCLASS_RESERVED_SLOTS_SHIFT 8 // room for 8 flags below */
|
||||
#define JSCLASS_RESERVED_SLOTS_WIDTH 8 // and 16 above this field */
|
||||
#define JSCLASS_RESERVED_SLOTS_MASK JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH)
|
||||
#define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \
|
||||
<< JSCLASS_RESERVED_SLOTS_SHIFT)
|
||||
#define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \
|
||||
>> JSCLASS_RESERVED_SLOTS_SHIFT) \
|
||||
& JSCLASS_RESERVED_SLOTS_MASK)
|
||||
|
||||
#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \
|
||||
JSCLASS_RESERVED_SLOTS_WIDTH)
|
||||
|
||||
#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
|
||||
#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
|
||||
#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
|
||||
#define JSCLASS_INTERNAL_FLAG3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
|
||||
|
||||
#define JSCLASS_IS_PROXY (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
|
||||
|
||||
#define JSCLASS_SKIP_NURSERY_FINALIZE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5))
|
||||
|
||||
// Reserved for embeddings.
|
||||
#define JSCLASS_USERBIT2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6))
|
||||
#define JSCLASS_USERBIT3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7))
|
||||
|
||||
#define JSCLASS_BACKGROUND_FINALIZE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+8))
|
||||
#define JSCLASS_FOREGROUND_FINALIZE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+9))
|
||||
|
||||
// Bits 26 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see
|
||||
// below.
|
||||
|
||||
// ECMA-262 requires that most constructors used internally create objects
|
||||
// with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
|
||||
// member initial value. The "original ... value" verbiage is there because
|
||||
// in ECMA-262, global properties naming class objects are read/write and
|
||||
// deleteable, for the most part.
|
||||
//
|
||||
// Implementing this efficiently requires that global objects have classes
|
||||
// with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
|
||||
// previously allowed, but is now an ES5 violation and thus unsupported.
|
||||
//
|
||||
// JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
|
||||
// the beginning of every global object's slots for use by the
|
||||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 39)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
|
||||
#define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp) \
|
||||
(((clasp)->flags & JSCLASS_IS_GLOBAL) \
|
||||
&& JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
|
||||
|
||||
// Fast access to the original value of each standard class's prototype.
|
||||
#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 10)
|
||||
#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(js::JSCLASS_CACHED_PROTO_WIDTH)
|
||||
#define JSCLASS_HAS_CACHED_PROTO(key) (uint32_t(key) << JSCLASS_CACHED_PROTO_SHIFT)
|
||||
#define JSCLASS_CACHED_PROTO_KEY(clasp) ((JSProtoKey) \
|
||||
(((clasp)->flags \
|
||||
>> JSCLASS_CACHED_PROTO_SHIFT) \
|
||||
& JSCLASS_CACHED_PROTO_MASK))
|
||||
|
||||
// Initializer for unused members of statically initialized JSClass structs.
|
||||
#define JSCLASS_NO_INTERNAL_MEMBERS {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
|
||||
#define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,JSCLASS_NO_INTERNAL_MEMBERS
|
||||
|
||||
namespace js {
|
||||
|
||||
struct Class
|
||||
{
|
||||
JS_CLASS_MEMBERS(js::ClassOps, FreeOp);
|
||||
const ClassSpec* spec;
|
||||
const ClassExtension* ext;
|
||||
const ObjectOps* oOps;
|
||||
|
||||
/*
|
||||
* Objects of this class aren't native objects. They don't have Shapes that
|
||||
* describe their properties and layout. Classes using this flag must
|
||||
* provide their own property behavior, either by being proxy classes (do
|
||||
* this) or by overriding all the ObjectOps except getElements, watch and
|
||||
* unwatch (don't do this).
|
||||
*/
|
||||
static const uint32_t NON_NATIVE = JSCLASS_INTERNAL_FLAG2;
|
||||
|
||||
bool isNative() const {
|
||||
return !(flags & NON_NATIVE);
|
||||
}
|
||||
|
||||
bool hasPrivate() const {
|
||||
return !!(flags & JSCLASS_HAS_PRIVATE);
|
||||
}
|
||||
|
||||
bool emulatesUndefined() const {
|
||||
return flags & JSCLASS_EMULATES_UNDEFINED;
|
||||
}
|
||||
|
||||
bool isJSFunction() const {
|
||||
return this == js::FunctionClassPtr;
|
||||
}
|
||||
|
||||
bool nonProxyCallable() const {
|
||||
MOZ_ASSERT(!isProxy());
|
||||
return isJSFunction() || getCall();
|
||||
}
|
||||
|
||||
bool isProxy() const {
|
||||
return flags & JSCLASS_IS_PROXY;
|
||||
}
|
||||
|
||||
bool isDOMClass() const {
|
||||
return flags & JSCLASS_IS_DOMJSCLASS;
|
||||
}
|
||||
|
||||
bool shouldDelayMetadataBuilder() const {
|
||||
return flags & JSCLASS_DELAY_METADATA_BUILDER;
|
||||
}
|
||||
|
||||
bool isWrappedNative() const {
|
||||
return flags & JSCLASS_IS_WRAPPED_NATIVE;
|
||||
}
|
||||
|
||||
static size_t offsetOfFlags() { return offsetof(Class, flags); }
|
||||
|
||||
bool specDefined() const { return spec ? spec->defined() : false; }
|
||||
JSProtoKey specInheritanceProtoKey()
|
||||
const { return spec ? spec->inheritanceProtoKey() : JSProto_Null; }
|
||||
bool specShouldDefineConstructor()
|
||||
const { return spec ? spec->shouldDefineConstructor() : true; }
|
||||
ClassObjectCreationOp specCreateConstructorHook()
|
||||
const { return spec ? spec->createConstructorHook() : nullptr; }
|
||||
ClassObjectCreationOp specCreatePrototypeHook()
|
||||
const { return spec ? spec->createPrototypeHook() : nullptr; }
|
||||
const JSFunctionSpec* specConstructorFunctions()
|
||||
const { return spec ? spec->constructorFunctions() : nullptr; }
|
||||
const JSPropertySpec* specConstructorProperties()
|
||||
const { return spec ? spec->constructorProperties() : nullptr; }
|
||||
const JSFunctionSpec* specPrototypeFunctions()
|
||||
const { return spec ? spec->prototypeFunctions() : nullptr; }
|
||||
const JSPropertySpec* specPrototypeProperties()
|
||||
const { return spec ? spec->prototypeProperties() : nullptr; }
|
||||
FinishClassInitOp specFinishInitHook()
|
||||
const { return spec ? spec->finishInitHook() : nullptr; }
|
||||
|
||||
JSWeakmapKeyDelegateOp extWeakmapKeyDelegateOp()
|
||||
const { return ext ? ext->weakmapKeyDelegateOp : nullptr; }
|
||||
JSObjectMovedOp extObjectMovedOp()
|
||||
const { return ext ? ext->objectMovedOp : nullptr; }
|
||||
|
||||
LookupPropertyOp getOpsLookupProperty() const { return oOps ? oOps->lookupProperty : nullptr; }
|
||||
DefinePropertyOp getOpsDefineProperty() const { return oOps ? oOps->defineProperty : nullptr; }
|
||||
HasPropertyOp getOpsHasProperty() const { return oOps ? oOps->hasProperty : nullptr; }
|
||||
GetPropertyOp getOpsGetProperty() const { return oOps ? oOps->getProperty : nullptr; }
|
||||
SetPropertyOp getOpsSetProperty() const { return oOps ? oOps->setProperty : nullptr; }
|
||||
GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
|
||||
const { return oOps ? oOps->getOwnPropertyDescriptor
|
||||
: nullptr; }
|
||||
DeletePropertyOp getOpsDeleteProperty() const { return oOps ? oOps->deleteProperty : nullptr; }
|
||||
WatchOp getOpsWatch() const { return oOps ? oOps->watch : nullptr; }
|
||||
UnwatchOp getOpsUnwatch() const { return oOps ? oOps->unwatch : nullptr; }
|
||||
GetElementsOp getOpsGetElements() const { return oOps ? oOps->getElements : nullptr; }
|
||||
JSNewEnumerateOp getOpsEnumerate() const { return oOps ? oOps->enumerate : nullptr; }
|
||||
JSFunToStringOp getOpsFunToString() const { return oOps ? oOps->funToString : nullptr; }
|
||||
};
|
||||
|
||||
static_assert(offsetof(JSClassOps, addProperty) == offsetof(ClassOps, addProperty),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, delProperty) == offsetof(ClassOps, delProperty),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, getProperty) == offsetof(ClassOps, getProperty),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, setProperty) == offsetof(ClassOps, setProperty),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, enumerate) == offsetof(ClassOps, enumerate),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, resolve) == offsetof(ClassOps, resolve),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, mayResolve) == offsetof(ClassOps, mayResolve),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, finalize) == offsetof(ClassOps, finalize),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, call) == offsetof(ClassOps, call),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, construct) == offsetof(ClassOps, construct),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, hasInstance) == offsetof(ClassOps, hasInstance),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(offsetof(JSClassOps, trace) == offsetof(ClassOps, trace),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
static_assert(sizeof(JSClassOps) == sizeof(ClassOps),
|
||||
"ClassOps and JSClassOps must be consistent");
|
||||
|
||||
static_assert(offsetof(JSClass, name) == offsetof(Class, name),
|
||||
"Class and JSClass must be consistent");
|
||||
static_assert(offsetof(JSClass, flags) == offsetof(Class, flags),
|
||||
"Class and JSClass must be consistent");
|
||||
static_assert(offsetof(JSClass, cOps) == offsetof(Class, cOps),
|
||||
"Class and JSClass must be consistent");
|
||||
static_assert(sizeof(JSClass) == sizeof(Class),
|
||||
"Class and JSClass must be consistent");
|
||||
|
||||
static MOZ_ALWAYS_INLINE const JSClass*
|
||||
Jsvalify(const Class* c)
|
||||
{
|
||||
return (const JSClass*)c;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE const Class*
|
||||
Valueify(const JSClass* c)
|
||||
{
|
||||
return (const Class*)c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration describing possible values of the [[Class]] internal property
|
||||
* value of objects.
|
||||
*/
|
||||
enum class ESClass {
|
||||
Object,
|
||||
Array,
|
||||
Number,
|
||||
String,
|
||||
Boolean,
|
||||
RegExp,
|
||||
ArrayBuffer,
|
||||
SharedArrayBuffer,
|
||||
Date,
|
||||
Set,
|
||||
Map,
|
||||
Promise,
|
||||
MapIterator,
|
||||
SetIterator,
|
||||
Arguments,
|
||||
Error,
|
||||
|
||||
/** None of the above. */
|
||||
Other
|
||||
};
|
||||
|
||||
/* Fills |vp| with the unboxed value for boxed types, or undefined otherwise. */
|
||||
bool
|
||||
Unbox(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp);
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FRIEND_API(bool)
|
||||
HasObjectMovedOp(JSObject* obj);
|
||||
#endif
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* js_Class_h */
|
||||
|
|
@ -1,581 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* ECMAScript conversion operations. */
|
||||
|
||||
#ifndef js_Conversions_h
|
||||
#define js_Conversions_h
|
||||
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Value.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace js {
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToBoolean. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToBooleanSlow(JS::HandleValue v);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToNumber. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToNumberSlow(JSContext* cx, JS::HandleValue v, double* dp);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToInt8. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToInt8Slow(JSContext *cx, JS::HandleValue v, int8_t *out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToUint8. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToUint8Slow(JSContext *cx, JS::HandleValue v, uint8_t *out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToInt16. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToInt16Slow(JSContext *cx, JS::HandleValue v, int16_t *out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToInt32. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToInt32Slow(JSContext* cx, JS::HandleValue v, int32_t* out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToUint32. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToUint32Slow(JSContext* cx, JS::HandleValue v, uint32_t* out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToUint16. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToUint16Slow(JSContext* cx, JS::HandleValue v, uint16_t* out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToInt64. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToInt64Slow(JSContext* cx, JS::HandleValue v, int64_t* out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToUint64. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToUint64Slow(JSContext* cx, JS::HandleValue v, uint64_t* out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToString. */
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
ToStringSlow(JSContext* cx, JS::HandleValue v);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToObject. */
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
ToObjectSlow(JSContext* cx, JS::HandleValue v, bool reportScanStack);
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
/**
|
||||
* Assert that we're not doing GC on cx, that we're in a request as
|
||||
* needed, and that the compartments for cx and v are correct.
|
||||
* Also check that GC would be safe at this point.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
AssertArgumentsAreSane(JSContext* cx, HandleValue v);
|
||||
#else
|
||||
inline void AssertArgumentsAreSane(JSContext* cx, HandleValue v)
|
||||
{}
|
||||
#endif /* JS_DEBUG */
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* ES6 draft 20141224, 7.1.1, second algorithm.
|
||||
*
|
||||
* Most users shouldn't call this -- use JS::ToBoolean, ToNumber, or ToString
|
||||
* instead. This will typically only be called from custom convert hooks that
|
||||
* wish to fall back to the ES6 default conversion behavior shared by most
|
||||
* objects in JS, codified as OrdinaryToPrimitive.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType type, MutableHandleValue vp);
|
||||
|
||||
/* ES6 draft 20141224, 7.1.2. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToBoolean(HandleValue v)
|
||||
{
|
||||
if (v.isBoolean())
|
||||
return v.toBoolean();
|
||||
if (v.isInt32())
|
||||
return v.toInt32() != 0;
|
||||
if (v.isNullOrUndefined())
|
||||
return false;
|
||||
if (v.isDouble()) {
|
||||
double d = v.toDouble();
|
||||
return !mozilla::IsNaN(d) && d != 0;
|
||||
}
|
||||
if (v.isSymbol())
|
||||
return true;
|
||||
|
||||
/* The slow path handles strings and objects. */
|
||||
return js::ToBooleanSlow(v);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, 7.1.3. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToNumber(JSContext* cx, HandleValue v, double* out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isNumber()) {
|
||||
*out = v.toNumber();
|
||||
return true;
|
||||
}
|
||||
return js::ToNumberSlow(cx, v, out);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, ToInteger (specialized for doubles). */
|
||||
inline double
|
||||
ToInteger(double d)
|
||||
{
|
||||
if (d == 0)
|
||||
return d;
|
||||
|
||||
if (!mozilla::IsFinite(d)) {
|
||||
if (mozilla::IsNaN(d))
|
||||
return 0;
|
||||
return d;
|
||||
}
|
||||
|
||||
return d < 0 ? ceil(d) : floor(d);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, 7.1.5. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToInt32(JSContext* cx, JS::HandleValue v, int32_t* out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = v.toInt32();
|
||||
return true;
|
||||
}
|
||||
return js::ToInt32Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, 7.1.6. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToUint32(JSContext* cx, HandleValue v, uint32_t* out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = uint32_t(v.toInt32());
|
||||
return true;
|
||||
}
|
||||
return js::ToUint32Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, 7.1.7. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToInt16(JSContext *cx, JS::HandleValue v, int16_t *out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = int16_t(v.toInt32());
|
||||
return true;
|
||||
}
|
||||
return js::ToInt16Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, 7.1.8. */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToUint16(JSContext* cx, HandleValue v, uint16_t* out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = uint16_t(v.toInt32());
|
||||
return true;
|
||||
}
|
||||
return js::ToUint16Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, 7.1.9 */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToInt8(JSContext *cx, JS::HandleValue v, int8_t *out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = int8_t(v.toInt32());
|
||||
return true;
|
||||
}
|
||||
return js::ToInt8Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/* ES6 ECMA-262, 7.1.10 */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToUint8(JSContext *cx, JS::HandleValue v, uint8_t *out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = uint8_t(v.toInt32());
|
||||
return true;
|
||||
}
|
||||
return js::ToUint8Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-standard, with behavior similar to that of ToInt32, except in its
|
||||
* producing an int64_t.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToInt64(JSContext* cx, HandleValue v, int64_t* out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = int64_t(v.toInt32());
|
||||
return true;
|
||||
}
|
||||
return js::ToInt64Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-standard, with behavior similar to that of ToUint32, except in its
|
||||
* producing a uint64_t.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToUint64(JSContext* cx, HandleValue v, uint64_t* out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = uint64_t(v.toInt32());
|
||||
return true;
|
||||
}
|
||||
return js::ToUint64Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, 7.1.12. */
|
||||
MOZ_ALWAYS_INLINE JSString*
|
||||
ToString(JSContext* cx, HandleValue v)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isString())
|
||||
return v.toString();
|
||||
return js::ToStringSlow(cx, v);
|
||||
}
|
||||
|
||||
/* ES6 draft 20141224, 7.1.13. */
|
||||
inline JSObject*
|
||||
ToObject(JSContext* cx, HandleValue v)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isObject())
|
||||
return &v.toObject();
|
||||
return js::ToObjectSlow(cx, v, false);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* Convert a double value to ResultType (an unsigned integral type) using
|
||||
* ECMAScript-style semantics (that is, in like manner to how ECMAScript's
|
||||
* ToInt32 converts to int32_t).
|
||||
*
|
||||
* If d is infinite or NaN, return 0.
|
||||
* Otherwise compute d2 = sign(d) * floor(abs(d)), and return the ResultType
|
||||
* value congruent to d2 mod 2**(bit width of ResultType).
|
||||
*
|
||||
* The algorithm below is inspired by that found in
|
||||
* <http://trac.webkit.org/changeset/67825/trunk/JavaScriptCore/runtime/JSValue.cpp>
|
||||
* but has been generalized to all integer widths.
|
||||
*/
|
||||
template<typename ResultType>
|
||||
inline ResultType
|
||||
ToUintWidth(double d)
|
||||
{
|
||||
static_assert(mozilla::IsUnsigned<ResultType>::value,
|
||||
"ResultType must be an unsigned type");
|
||||
|
||||
uint64_t bits = mozilla::BitwiseCast<uint64_t>(d);
|
||||
unsigned DoubleExponentShift = mozilla::FloatingPoint<double>::kExponentShift;
|
||||
|
||||
// Extract the exponent component. (Be careful here! It's not technically
|
||||
// the exponent in NaN, infinities, and subnormals.)
|
||||
int_fast16_t exp =
|
||||
int_fast16_t((bits & mozilla::FloatingPoint<double>::kExponentBits) >> DoubleExponentShift) -
|
||||
int_fast16_t(mozilla::FloatingPoint<double>::kExponentBias);
|
||||
|
||||
// If the exponent's less than zero, abs(d) < 1, so the result is 0. (This
|
||||
// also handles subnormals.)
|
||||
if (exp < 0)
|
||||
return 0;
|
||||
|
||||
uint_fast16_t exponent = mozilla::AssertedCast<uint_fast16_t>(exp);
|
||||
|
||||
// If the exponent is greater than or equal to the bits of precision of a
|
||||
// double plus ResultType's width, the number is either infinite, NaN, or
|
||||
// too large to have lower-order bits in the congruent value. (Example:
|
||||
// 2**84 is exactly representable as a double. The next exact double is
|
||||
// 2**84 + 2**32. Thus if ResultType is int32_t, an exponent >= 84 implies
|
||||
// floor(abs(d)) == 0 mod 2**32.) Return 0 in all these cases.
|
||||
const size_t ResultWidth = CHAR_BIT * sizeof(ResultType);
|
||||
if (exponent >= DoubleExponentShift + ResultWidth)
|
||||
return 0;
|
||||
|
||||
// The significand contains the bits that will determine the final result.
|
||||
// Shift those bits left or right, according to the exponent, to their
|
||||
// locations in the unsigned binary representation of floor(abs(d)).
|
||||
static_assert(sizeof(ResultType) <= sizeof(uint64_t),
|
||||
"Left-shifting below would lose upper bits");
|
||||
ResultType result = (exponent > DoubleExponentShift)
|
||||
? ResultType(bits << (exponent - DoubleExponentShift))
|
||||
: ResultType(bits >> (DoubleExponentShift - exponent));
|
||||
|
||||
// Two further complications remain. First, |result| may contain bogus
|
||||
// sign/exponent bits. Second, IEEE-754 numbers' significands (excluding
|
||||
// subnormals, but we already handled those) have an implicit leading 1
|
||||
// which may affect the final result.
|
||||
//
|
||||
// It may appear that there's complexity here depending on how ResultWidth
|
||||
// and DoubleExponentShift relate, but it turns out there's not.
|
||||
//
|
||||
// Assume ResultWidth < DoubleExponentShift:
|
||||
// Only right-shifts leave bogus bits in |result|. For this to happen,
|
||||
// we must right-shift by > |DoubleExponentShift - ResultWidth|, implying
|
||||
// |exponent < ResultWidth|.
|
||||
// The implicit leading bit only matters if it appears in the final
|
||||
// result -- if |2**exponent mod 2**ResultWidth != 0|. This implies
|
||||
// |exponent < ResultWidth|.
|
||||
// Otherwise assume ResultWidth >= DoubleExponentShift:
|
||||
// Any left-shift less than |ResultWidth - DoubleExponentShift| leaves
|
||||
// bogus bits in |result|. This implies |exponent < ResultWidth|. Any
|
||||
// right-shift less than |ResultWidth| does too, which implies
|
||||
// |DoubleExponentShift - ResultWidth < exponent|. By assumption, then,
|
||||
// |exponent| is negative, but we excluded that above. So bogus bits
|
||||
// need only |exponent < ResultWidth|.
|
||||
// The implicit leading bit matters identically to the other case, so
|
||||
// again, |exponent < ResultWidth|.
|
||||
if (exponent < ResultWidth) {
|
||||
ResultType implicitOne = ResultType(1) << exponent;
|
||||
result &= implicitOne - 1; // remove bogus bits
|
||||
result += implicitOne; // add the implicit bit
|
||||
}
|
||||
|
||||
// Compute the congruent value in the signed range.
|
||||
return (bits & mozilla::FloatingPoint<double>::kSignBit) ? ~result + 1 : result;
|
||||
}
|
||||
|
||||
template<typename ResultType>
|
||||
inline ResultType
|
||||
ToIntWidth(double d)
|
||||
{
|
||||
static_assert(mozilla::IsSigned<ResultType>::value,
|
||||
"ResultType must be a signed type");
|
||||
|
||||
const ResultType MaxValue = (1ULL << (CHAR_BIT * sizeof(ResultType) - 1)) - 1;
|
||||
const ResultType MinValue = -MaxValue - 1;
|
||||
|
||||
typedef typename mozilla::MakeUnsigned<ResultType>::Type UnsignedResult;
|
||||
UnsignedResult u = ToUintWidth<UnsignedResult>(d);
|
||||
if (u <= UnsignedResult(MaxValue))
|
||||
return static_cast<ResultType>(u);
|
||||
return (MinValue + static_cast<ResultType>(u - MaxValue)) - 1;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/* ES5 9.5 ToInt32 (specialized for doubles). */
|
||||
inline int32_t
|
||||
ToInt32(double d)
|
||||
{
|
||||
// clang crashes compiling this when targeting arm:
|
||||
// https://llvm.org/bugs/show_bug.cgi?id=22974
|
||||
#if defined (__arm__) && defined (__GNUC__) && !defined(__clang__)
|
||||
int32_t i;
|
||||
uint32_t tmp0;
|
||||
uint32_t tmp1;
|
||||
uint32_t tmp2;
|
||||
asm (
|
||||
// We use a pure integer solution here. In the 'softfp' ABI, the argument
|
||||
// will start in r0 and r1, and VFP can't do all of the necessary ECMA
|
||||
// conversions by itself so some integer code will be required anyway. A
|
||||
// hybrid solution is faster on A9, but this pure integer solution is
|
||||
// notably faster for A8.
|
||||
|
||||
// %0 is the result register, and may alias either of the %[QR]1 registers.
|
||||
// %Q4 holds the lower part of the mantissa.
|
||||
// %R4 holds the sign, exponent, and the upper part of the mantissa.
|
||||
// %1, %2 and %3 are used as temporary values.
|
||||
|
||||
// Extract the exponent.
|
||||
" mov %1, %R4, LSR #20\n"
|
||||
" bic %1, %1, #(1 << 11)\n" // Clear the sign.
|
||||
|
||||
// Set the implicit top bit of the mantissa. This clobbers a bit of the
|
||||
// exponent, but we have already extracted that.
|
||||
" orr %R4, %R4, #(1 << 20)\n"
|
||||
|
||||
// Special Cases
|
||||
// We should return zero in the following special cases:
|
||||
// - Exponent is 0x000 - 1023: +/-0 or subnormal.
|
||||
// - Exponent is 0x7ff - 1023: +/-INFINITY or NaN
|
||||
// - This case is implicitly handled by the standard code path anyway,
|
||||
// as shifting the mantissa up by the exponent will result in '0'.
|
||||
//
|
||||
// The result is composed of the mantissa, prepended with '1' and
|
||||
// bit-shifted left by the (decoded) exponent. Note that because the r1[20]
|
||||
// is the bit with value '1', r1 is effectively already shifted (left) by
|
||||
// 20 bits, and r0 is already shifted by 52 bits.
|
||||
|
||||
// Adjust the exponent to remove the encoding offset. If the decoded
|
||||
// exponent is negative, quickly bail out with '0' as such values round to
|
||||
// zero anyway. This also catches +/-0 and subnormals.
|
||||
" sub %1, %1, #0xff\n"
|
||||
" subs %1, %1, #0x300\n"
|
||||
" bmi 8f\n"
|
||||
|
||||
// %1 = (decoded) exponent >= 0
|
||||
// %R4 = upper mantissa and sign
|
||||
|
||||
// ---- Lower Mantissa ----
|
||||
" subs %3, %1, #52\n" // Calculate exp-52
|
||||
" bmi 1f\n"
|
||||
|
||||
// Shift r0 left by exp-52.
|
||||
// Ensure that we don't overflow ARM's 8-bit shift operand range.
|
||||
// We need to handle anything up to an 11-bit value here as we know that
|
||||
// 52 <= exp <= 1024 (0x400). Any shift beyond 31 bits results in zero
|
||||
// anyway, so as long as we don't touch the bottom 5 bits, we can use
|
||||
// a logical OR to push long shifts into the 32 <= (exp&0xff) <= 255 range.
|
||||
" bic %2, %3, #0xff\n"
|
||||
" orr %3, %3, %2, LSR #3\n"
|
||||
// We can now perform a straight shift, avoiding the need for any
|
||||
// conditional instructions or extra branches.
|
||||
" mov %Q4, %Q4, LSL %3\n"
|
||||
" b 2f\n"
|
||||
"1:\n" // Shift r0 right by 52-exp.
|
||||
// We know that 0 <= exp < 52, and we can shift up to 255 bits so 52-exp
|
||||
// will always be a valid shift and we can sk%3 the range check for this case.
|
||||
" rsb %3, %1, #52\n"
|
||||
" mov %Q4, %Q4, LSR %3\n"
|
||||
|
||||
// %1 = (decoded) exponent
|
||||
// %R4 = upper mantissa and sign
|
||||
// %Q4 = partially-converted integer
|
||||
|
||||
"2:\n"
|
||||
// ---- Upper Mantissa ----
|
||||
// This is much the same as the lower mantissa, with a few different
|
||||
// boundary checks and some masking to hide the exponent & sign bit in the
|
||||
// upper word.
|
||||
// Note that the upper mantissa is pre-shifted by 20 in %R4, but we shift
|
||||
// it left more to remove the sign and exponent so it is effectively
|
||||
// pre-shifted by 31 bits.
|
||||
" subs %3, %1, #31\n" // Calculate exp-31
|
||||
" mov %1, %R4, LSL #11\n" // Re-use %1 as a temporary register.
|
||||
" bmi 3f\n"
|
||||
|
||||
// Shift %R4 left by exp-31.
|
||||
// Avoid overflowing the 8-bit shift range, as before.
|
||||
" bic %2, %3, #0xff\n"
|
||||
" orr %3, %3, %2, LSR #3\n"
|
||||
// Perform the shift.
|
||||
" mov %2, %1, LSL %3\n"
|
||||
" b 4f\n"
|
||||
"3:\n" // Shift r1 right by 31-exp.
|
||||
// We know that 0 <= exp < 31, and we can shift up to 255 bits so 31-exp
|
||||
// will always be a valid shift and we can skip the range check for this case.
|
||||
" rsb %3, %3, #0\n" // Calculate 31-exp from -(exp-31)
|
||||
" mov %2, %1, LSR %3\n" // Thumb-2 can't do "LSR %3" in "orr".
|
||||
|
||||
// %Q4 = partially-converted integer (lower)
|
||||
// %R4 = upper mantissa and sign
|
||||
// %2 = partially-converted integer (upper)
|
||||
|
||||
"4:\n"
|
||||
// Combine the converted parts.
|
||||
" orr %Q4, %Q4, %2\n"
|
||||
// Negate the result if we have to, and move it to %0 in the process. To
|
||||
// avoid conditionals, we can do this by inverting on %R4[31], then adding
|
||||
// %R4[31]>>31.
|
||||
" eor %Q4, %Q4, %R4, ASR #31\n"
|
||||
" add %0, %Q4, %R4, LSR #31\n"
|
||||
" b 9f\n"
|
||||
"8:\n"
|
||||
// +/-INFINITY, +/-0, subnormals, NaNs, and anything else out-of-range that
|
||||
// will result in a conversion of '0'.
|
||||
" mov %0, #0\n"
|
||||
"9:\n"
|
||||
: "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2), "=&r" (d)
|
||||
: "4" (d)
|
||||
: "cc"
|
||||
);
|
||||
return i;
|
||||
#else
|
||||
return detail::ToIntWidth<int32_t>(d);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ES5 9.6 (specialized for doubles). */
|
||||
inline uint32_t
|
||||
ToUint32(double d)
|
||||
{
|
||||
return detail::ToUintWidth<uint32_t>(d);
|
||||
}
|
||||
|
||||
/* WEBIDL 4.2.4 */
|
||||
inline int8_t
|
||||
ToInt8(double d)
|
||||
{
|
||||
return detail::ToIntWidth<int8_t>(d);
|
||||
}
|
||||
|
||||
/* ECMA-262 7.1.10 ToUInt8() specialized for doubles. */
|
||||
inline int8_t
|
||||
ToUint8(double d)
|
||||
{
|
||||
return detail::ToUintWidth<uint8_t>(d);
|
||||
}
|
||||
|
||||
/* WEBIDL 4.2.6 */
|
||||
inline int16_t
|
||||
ToInt16(double d)
|
||||
{
|
||||
return detail::ToIntWidth<int16_t>(d);
|
||||
}
|
||||
|
||||
inline uint16_t
|
||||
ToUint16(double d)
|
||||
{
|
||||
return detail::ToUintWidth<uint16_t>(d);
|
||||
}
|
||||
|
||||
/* WEBIDL 4.2.10 */
|
||||
inline int64_t
|
||||
ToInt64(double d)
|
||||
{
|
||||
return detail::ToIntWidth<int64_t>(d);
|
||||
}
|
||||
|
||||
/* WEBIDL 4.2.11 */
|
||||
inline uint64_t
|
||||
ToUint64(double d)
|
||||
{
|
||||
return detail::ToUintWidth<uint64_t>(d);
|
||||
}
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_Conversions_h */
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; 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/. */
|
||||
|
||||
/* JavaScript date/time computation and creation functions. */
|
||||
|
||||
#ifndef js_Date_h
|
||||
#define js_Date_h
|
||||
|
||||
/*
|
||||
* Dates in JavaScript are defined by IEEE-754 double precision numbers from
|
||||
* the set:
|
||||
*
|
||||
* { t ∈ ℕ : -8.64e15 ≤ t ≤ +8.64e15 } ∪ { NaN }
|
||||
*
|
||||
* The single NaN value represents any invalid-date value. All other values
|
||||
* represent idealized durations in milliseconds since the UTC epoch. (Leap
|
||||
* seconds are ignored; leap days are not.) +0 is the only zero in this set.
|
||||
* The limit represented by 8.64e15 milliseconds is 100 million days either
|
||||
* side of 00:00 January 1, 1970 UTC.
|
||||
*
|
||||
* Dates in the above set are represented by the |ClippedTime| class. The
|
||||
* double type is a superset of the above set, so it *may* (but need not)
|
||||
* represent a date. Use ECMAScript's |TimeClip| method to produce a date from
|
||||
* a double.
|
||||
*
|
||||
* Date *objects* are simply wrappers around |TimeClip|'d numbers, with a bunch
|
||||
* of accessor methods to the various aspects of the represented date.
|
||||
*/
|
||||
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include "js/Conversions.h"
|
||||
#include "js/Value.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace JS {
|
||||
|
||||
/**
|
||||
* Re-query the system to determine the current time zone adjustment from UTC,
|
||||
* including any component due to DST. If the time zone has changed, this will
|
||||
* cause all Date object non-UTC methods and formatting functions to produce
|
||||
* appropriately adjusted results.
|
||||
*
|
||||
* Left to its own devices, SpiderMonkey itself may occasionally call this
|
||||
* method to attempt to keep up with system time changes. However, no
|
||||
* particular frequency of checking is guaranteed. Embedders unable to accept
|
||||
* occasional inaccuracies should call this method in response to system time
|
||||
* changes, or immediately before operations requiring instantaneous
|
||||
* correctness, to guarantee correct behavior.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
ResetTimeZone();
|
||||
|
||||
class ClippedTime;
|
||||
inline ClippedTime TimeClip(double time);
|
||||
|
||||
/*
|
||||
* |ClippedTime| represents the limited subset of dates/times described above.
|
||||
*
|
||||
* An invalid date/time may be created through the |ClippedTime::invalid|
|
||||
* method. Otherwise, a |ClippedTime| may be created using the |TimeClip|
|
||||
* method.
|
||||
*
|
||||
* In typical use, the user might wish to manipulate a timestamp. The user
|
||||
* performs a series of operations on it, but the final value might not be a
|
||||
* date as defined above -- it could have overflowed, acquired a fractional
|
||||
* component, &c. So as a *final* step, the user passes that value through
|
||||
* |TimeClip| to produce a number restricted to JavaScript's date range.
|
||||
*
|
||||
* APIs that accept a JavaScript date value thus accept a |ClippedTime|, not a
|
||||
* double. This ensures that date/time APIs will only ever receive acceptable
|
||||
* JavaScript dates. This also forces users to perform any desired clipping,
|
||||
* as only the user knows what behavior is desired when clipping occurs.
|
||||
*/
|
||||
class ClippedTime
|
||||
{
|
||||
double t;
|
||||
|
||||
explicit ClippedTime(double time) : t(time) {}
|
||||
friend ClippedTime TimeClip(double time);
|
||||
|
||||
public:
|
||||
// Create an invalid date.
|
||||
ClippedTime() : t(mozilla::UnspecifiedNaN<double>()) {}
|
||||
|
||||
// Create an invalid date/time, more explicitly; prefer this to the default
|
||||
// constructor.
|
||||
static ClippedTime invalid() { return ClippedTime(); }
|
||||
|
||||
double toDouble() const { return t; }
|
||||
|
||||
bool isValid() const { return !mozilla::IsNaN(t); }
|
||||
};
|
||||
|
||||
// ES6 20.3.1.15.
|
||||
//
|
||||
// Clip a double to JavaScript's date range (or to an invalid date) using the
|
||||
// ECMAScript TimeClip algorithm.
|
||||
inline ClippedTime
|
||||
TimeClip(double time)
|
||||
{
|
||||
// Steps 1-2.
|
||||
const double MaxTimeMagnitude = 8.64e15;
|
||||
if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude)
|
||||
return ClippedTime(mozilla::UnspecifiedNaN<double>());
|
||||
|
||||
// Step 3.
|
||||
return ClippedTime(ToInteger(time) + (+0.0));
|
||||
}
|
||||
|
||||
// Produce a double Value from the given time. Because times may be NaN,
|
||||
// prefer using this to manual canonicalization.
|
||||
inline Value
|
||||
TimeValue(ClippedTime time)
|
||||
{
|
||||
return DoubleValue(JS::CanonicalizeNaN(time.toDouble()));
|
||||
}
|
||||
|
||||
// Create a new Date object whose [[DateValue]] internal slot contains the
|
||||
// clipped |time|. (Users who must represent times outside that range must use
|
||||
// another representation.)
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
NewDateObject(JSContext* cx, ClippedTime time);
|
||||
|
||||
// Year is a year, month is 0-11, day is 1-based. The return value is a number
|
||||
// of milliseconds since the epoch.
|
||||
//
|
||||
// Consistent with the MakeDate algorithm defined in ECMAScript, this value is
|
||||
// *not* clipped! Use JS::TimeClip if you need a clipped date.
|
||||
JS_PUBLIC_API(double)
|
||||
MakeDate(double year, unsigned month, unsigned day);
|
||||
|
||||
// Takes an integer number of milliseconds since the epoch and returns the
|
||||
// year. Can return NaN, and will do so if NaN is passed in.
|
||||
JS_PUBLIC_API(double)
|
||||
YearFromTime(double time);
|
||||
|
||||
// Takes an integer number of milliseconds since the epoch and returns the
|
||||
// month (0-11). Can return NaN, and will do so if NaN is passed in.
|
||||
JS_PUBLIC_API(double)
|
||||
MonthFromTime(double time);
|
||||
|
||||
// Takes an integer number of milliseconds since the epoch and returns the
|
||||
// day (1-based). Can return NaN, and will do so if NaN is passed in.
|
||||
JS_PUBLIC_API(double)
|
||||
DayFromTime(double time);
|
||||
|
||||
// Takes an integer year and returns the number of days from epoch to the given
|
||||
// year.
|
||||
// NOTE: The calculation performed by this function is literally that given in
|
||||
// the ECMAScript specification. Nonfinite years, years containing fractional
|
||||
// components, and years outside ECMAScript's date range are not handled with
|
||||
// any particular intelligence. Garbage in, garbage out.
|
||||
JS_PUBLIC_API(double)
|
||||
DayFromYear(double year);
|
||||
|
||||
// Takes an integer number of milliseconds since the epoch and an integer year,
|
||||
// returns the number of days in that year. If |time| is nonfinite, returns NaN.
|
||||
// Otherwise |time| *must* correspond to a time within the valid year |year|.
|
||||
// This should usually be ensured by computing |year| as |JS::DayFromYear(time)|.
|
||||
JS_PUBLIC_API(double)
|
||||
DayWithinYear(double time, double year);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_Date_h */
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
// Interfaces by which the embedding can interact with the Debugger API.
|
||||
|
||||
#ifndef js_Debug_h
|
||||
#define js_Debug_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
namespace js {
|
||||
class Debugger;
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
namespace dbg {
|
||||
|
||||
// Helping embedding code build objects for Debugger
|
||||
// -------------------------------------------------
|
||||
//
|
||||
// Some Debugger API features lean on the embedding application to construct
|
||||
// their result values. For example, Debugger.Frame.prototype.scriptEntryReason
|
||||
// calls hooks provided by the embedding to construct values explaining why it
|
||||
// invoked JavaScript; if F is a frame called from a mouse click event handler,
|
||||
// F.scriptEntryReason would return an object of the form:
|
||||
//
|
||||
// { eventType: "mousedown", event: <object> }
|
||||
//
|
||||
// where <object> is a Debugger.Object whose referent is the event being
|
||||
// dispatched.
|
||||
//
|
||||
// However, Debugger implements a trust boundary. Debuggee code may be
|
||||
// considered untrusted; debugger code needs to be protected from debuggee
|
||||
// getters, setters, proxies, Object.watch watchpoints, and any other feature
|
||||
// that might accidentally cause debugger code to set the debuggee running. The
|
||||
// Debugger API tries to make it easy to write safe debugger code by only
|
||||
// offering access to debuggee objects via Debugger.Object instances, which
|
||||
// ensure that only those operations whose explicit purpose is to invoke
|
||||
// debuggee code do so. But this protective membrane is only helpful if we
|
||||
// interpose Debugger.Object instances in all the necessary spots.
|
||||
//
|
||||
// SpiderMonkey's compartment system also implements a trust boundary. The
|
||||
// debuggee and debugger are always in different compartments. Inter-compartment
|
||||
// work requires carefully tracking which compartment each JSObject or JS::Value
|
||||
// belongs to, and ensuring that is is correctly wrapped for each operation.
|
||||
//
|
||||
// It seems precarious to expect the embedding's hooks to implement these trust
|
||||
// boundaries. Instead, the JS::dbg::Builder API segregates the code which
|
||||
// constructs trusted objects from that which deals with untrusted objects.
|
||||
// Trusted objects have an entirely different C++ type, so code that improperly
|
||||
// mixes trusted and untrusted objects is caught at compile time.
|
||||
//
|
||||
// In the structure shown above, there are two trusted objects, and one
|
||||
// untrusted object:
|
||||
//
|
||||
// - The overall object, with the 'eventType' and 'event' properties, is a
|
||||
// trusted object. We're going to return it to D.F.p.scriptEntryReason's
|
||||
// caller, which will handle it directly.
|
||||
//
|
||||
// - The Debugger.Object instance appearing as the value of the 'event' property
|
||||
// is a trusted object. It belongs to the same Debugger instance as the
|
||||
// Debugger.Frame instance whose scriptEntryReason accessor was called, and
|
||||
// presents a safe reflection-oriented API for inspecting its referent, which
|
||||
// is:
|
||||
//
|
||||
// - The actual event object, an untrusted object, and the referent of the
|
||||
// Debugger.Object above. (Content can do things like replacing accessors on
|
||||
// Event.prototype.)
|
||||
//
|
||||
// Using JS::dbg::Builder, all objects and values the embedding deals with
|
||||
// directly are considered untrusted, and are assumed to be debuggee values. The
|
||||
// only way to construct trusted objects is to use Builder's own methods, which
|
||||
// return a separate Object type. The only way to set a property on a trusted
|
||||
// object is through that Object type. The actual trusted object is never
|
||||
// exposed to the embedding.
|
||||
//
|
||||
// So, for example, the embedding might use code like the following to construct
|
||||
// the object shown above, given a Builder passed to it by Debugger:
|
||||
//
|
||||
// bool
|
||||
// MyScriptEntryReason::explain(JSContext* cx,
|
||||
// Builder& builder,
|
||||
// Builder::Object& result)
|
||||
// {
|
||||
// JSObject* eventObject = ... obtain debuggee event object somehow ...;
|
||||
// if (!eventObject)
|
||||
// return false;
|
||||
// result = builder.newObject(cx);
|
||||
// return result &&
|
||||
// result.defineProperty(cx, "eventType", SafelyFetchType(eventObject)) &&
|
||||
// result.defineProperty(cx, "event", eventObject);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// Object::defineProperty also accepts an Object as the value to store on the
|
||||
// property. By its type, we know that the value is trusted, so we set it
|
||||
// directly as the property's value, without interposing a Debugger.Object
|
||||
// wrapper. This allows the embedding to builted nested structures of trusted
|
||||
// objects.
|
||||
//
|
||||
// The Builder and Builder::Object methods take care of doing whatever
|
||||
// compartment switching and wrapping are necessary to construct the trusted
|
||||
// values in the Debugger's compartment.
|
||||
//
|
||||
// The Object type is self-rooting. Construction, assignment, and destruction
|
||||
// all properly root the referent object.
|
||||
|
||||
class BuilderOrigin;
|
||||
|
||||
class Builder {
|
||||
// The Debugger instance whose client we are building a value for. We build
|
||||
// objects in this object's compartment.
|
||||
PersistentRootedObject debuggerObject;
|
||||
|
||||
// debuggerObject's Debugger structure, for convenience.
|
||||
js::Debugger* debugger;
|
||||
|
||||
// Check that |thing| is in the same compartment as our debuggerObject. Used
|
||||
// for assertions when constructing BuiltThings. We can overload this as we
|
||||
// add more instantiations of BuiltThing.
|
||||
#if DEBUG
|
||||
void assertBuilt(JSObject* obj);
|
||||
#else
|
||||
void assertBuilt(JSObject* obj) { }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// A reference to a trusted object or value. At the moment, we only use it
|
||||
// with JSObject*.
|
||||
template<typename T>
|
||||
class BuiltThing {
|
||||
friend class BuilderOrigin;
|
||||
|
||||
protected:
|
||||
// The Builder to which this trusted thing belongs.
|
||||
Builder& owner;
|
||||
|
||||
// A rooted reference to our value.
|
||||
PersistentRooted<T> value;
|
||||
|
||||
BuiltThing(JSContext* cx, Builder& owner_, T value_ = GCPolicy<T>::initial())
|
||||
: owner(owner_), value(cx, value_)
|
||||
{
|
||||
owner.assertBuilt(value_);
|
||||
}
|
||||
|
||||
// Forward some things from our owner, for convenience.
|
||||
js::Debugger* debugger() const { return owner.debugger; }
|
||||
JSObject* debuggerObject() const { return owner.debuggerObject; }
|
||||
|
||||
public:
|
||||
BuiltThing(const BuiltThing& rhs) : owner(rhs.owner), value(rhs.value) { }
|
||||
BuiltThing& operator=(const BuiltThing& rhs) {
|
||||
MOZ_ASSERT(&owner == &rhs.owner);
|
||||
owner.assertBuilt(rhs.value);
|
||||
value = rhs.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
// If we ever instantiate BuiltThing<Value>, this might not suffice.
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
BuiltThing() = delete;
|
||||
};
|
||||
|
||||
public:
|
||||
// A reference to a trusted object, possibly null. Instances of Object are
|
||||
// always properly rooted. They can be copied and assigned, as if they were
|
||||
// pointers.
|
||||
class Object: private BuiltThing<JSObject*> {
|
||||
friend class Builder; // for construction
|
||||
friend class BuilderOrigin; // for unwrapping
|
||||
|
||||
typedef BuiltThing<JSObject*> Base;
|
||||
|
||||
// This is private, because only Builders can create Objects that
|
||||
// actually point to something (hence the 'friend' declaration).
|
||||
Object(JSContext* cx, Builder& owner_, HandleObject obj) : Base(cx, owner_, obj.get()) { }
|
||||
|
||||
bool definePropertyToTrusted(JSContext* cx, const char* name,
|
||||
JS::MutableHandleValue value);
|
||||
|
||||
public:
|
||||
Object(JSContext* cx, Builder& owner_) : Base(cx, owner_, nullptr) { }
|
||||
Object(const Object& rhs) : Base(rhs) { }
|
||||
|
||||
// Our automatically-generated assignment operator can see our base
|
||||
// class's assignment operator, so we don't need to write one out here.
|
||||
|
||||
// Set the property named |name| on this object to |value|.
|
||||
//
|
||||
// If |value| is a string or primitive, re-wrap it for the debugger's
|
||||
// compartment.
|
||||
//
|
||||
// If |value| is an object, assume it is a debuggee object and make a
|
||||
// Debugger.Object instance referring to it. Set that as the propery's
|
||||
// value.
|
||||
//
|
||||
// If |value| is another trusted object, store it directly as the
|
||||
// property's value.
|
||||
//
|
||||
// On error, report the problem on cx and return false.
|
||||
bool defineProperty(JSContext* cx, const char* name, JS::HandleValue value);
|
||||
bool defineProperty(JSContext* cx, const char* name, JS::HandleObject value);
|
||||
bool defineProperty(JSContext* cx, const char* name, Object& value);
|
||||
|
||||
using Base::operator bool;
|
||||
};
|
||||
|
||||
// Build an empty object for direct use by debugger code, owned by this
|
||||
// Builder. If an error occurs, report it on cx and return a false Object.
|
||||
Object newObject(JSContext* cx);
|
||||
|
||||
protected:
|
||||
Builder(JSContext* cx, js::Debugger* debugger);
|
||||
};
|
||||
|
||||
// Debugger itself instantiates this subclass of Builder, which can unwrap
|
||||
// BuiltThings that belong to it.
|
||||
class BuilderOrigin : public Builder {
|
||||
template<typename T>
|
||||
T unwrapAny(const BuiltThing<T>& thing) {
|
||||
MOZ_ASSERT(&thing.owner == this);
|
||||
return thing.value.get();
|
||||
}
|
||||
|
||||
public:
|
||||
BuilderOrigin(JSContext* cx, js::Debugger* debugger_)
|
||||
: Builder(cx, debugger_)
|
||||
{ }
|
||||
|
||||
JSObject* unwrap(Object& object) { return unwrapAny(object); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Finding the size of blocks allocated with malloc
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Debugger.Memory wants to be able to report how many bytes items in memory are
|
||||
// consuming. To do this, it needs a function that accepts a pointer to a block,
|
||||
// and returns the number of bytes allocated to that block. SpiderMonkey itself
|
||||
// doesn't know which function is appropriate to use, but the embedding does.
|
||||
|
||||
// Tell Debuggers in |cx| to use |mallocSizeOf| to find the size of
|
||||
// malloc'd blocks.
|
||||
JS_PUBLIC_API(void)
|
||||
SetDebuggerMallocSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
// Get the MallocSizeOf function that the given context is using to find the
|
||||
// size of malloc'd blocks.
|
||||
JS_PUBLIC_API(mozilla::MallocSizeOf)
|
||||
GetDebuggerMallocSizeOf(JSContext* cx);
|
||||
|
||||
|
||||
|
||||
// Debugger and Garbage Collection Events
|
||||
// --------------------------------------
|
||||
//
|
||||
// The Debugger wants to report about its debuggees' GC cycles, however entering
|
||||
// JS after a GC is troublesome since SpiderMonkey will often do something like
|
||||
// force a GC and then rely on the nursery being empty. If we call into some
|
||||
// Debugger's hook after the GC, then JS runs and the nursery won't be
|
||||
// empty. Instead, we rely on embedders to call back into SpiderMonkey after a
|
||||
// GC and notify Debuggers to call their onGarbageCollection hook.
|
||||
|
||||
|
||||
// For each Debugger that observed a debuggee involved in the given GC event,
|
||||
// call its `onGarbageCollection` hook.
|
||||
JS_PUBLIC_API(bool)
|
||||
FireOnGarbageCollectionHook(JSContext* cx, GarbageCollectionEvent::Ptr&& data);
|
||||
|
||||
|
||||
|
||||
// Handlers for observing Promises
|
||||
// -------------------------------
|
||||
//
|
||||
// The Debugger wants to observe behavior of promises, which are implemented by
|
||||
// Gecko with webidl and which SpiderMonkey knows nothing about. On the other
|
||||
// hand, Gecko knows nothing about which (if any) debuggers are observing a
|
||||
// promise's global. The compromise is that Gecko is responsible for calling
|
||||
// these handlers at the appropriate times, and SpiderMonkey will handle
|
||||
// notifying any Debugger instances that are observing the given promise's
|
||||
// global.
|
||||
|
||||
// Notify any Debugger instances observing this promise's global that a new
|
||||
// promise was allocated.
|
||||
JS_PUBLIC_API(void)
|
||||
onNewPromise(JSContext* cx, HandleObject promise);
|
||||
|
||||
// Notify any Debugger instances observing this promise's global that the
|
||||
// promise has settled (ie, it has either been fulfilled or rejected). Note that
|
||||
// this is *not* equivalent to the promise resolution (ie, the promise's fate
|
||||
// getting locked in) because you can resolve a promise with another pending
|
||||
// promise, in which case neither promise has settled yet.
|
||||
//
|
||||
// It is Gecko's responsibility to ensure that this is never called on the same
|
||||
// promise more than once (because a promise can only make the transition from
|
||||
// unsettled to settled once).
|
||||
JS_PUBLIC_API(void)
|
||||
onPromiseSettled(JSContext* cx, HandleObject promise);
|
||||
|
||||
|
||||
|
||||
// Return true if the given value is a Debugger object, false otherwise.
|
||||
JS_PUBLIC_API(bool)
|
||||
IsDebugger(JSObject& obj);
|
||||
|
||||
// Append each of the debuggee global objects observed by the Debugger object
|
||||
// |dbgObj| to |vector|. Returns true on success, false on failure.
|
||||
JS_PUBLIC_API(bool)
|
||||
GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj, AutoObjectVector& vector);
|
||||
|
||||
|
||||
// Hooks for reporting where JavaScript execution began.
|
||||
//
|
||||
// Our performance tools would like to be able to label blocks of JavaScript
|
||||
// execution with the function name and source location where execution began:
|
||||
// the event handler, the callback, etc.
|
||||
//
|
||||
// Construct an instance of this class on the stack, providing a JSContext
|
||||
// belonging to the runtime in which execution will occur. Each time we enter
|
||||
// JavaScript --- specifically, each time we push a JavaScript stack frame that
|
||||
// has no older JS frames younger than this AutoEntryMonitor --- we will
|
||||
// call the appropriate |Entry| member function to indicate where we've begun
|
||||
// execution.
|
||||
|
||||
class MOZ_STACK_CLASS AutoEntryMonitor {
|
||||
JSRuntime* runtime_;
|
||||
AutoEntryMonitor* savedMonitor_;
|
||||
|
||||
public:
|
||||
explicit AutoEntryMonitor(JSContext* cx);
|
||||
~AutoEntryMonitor();
|
||||
|
||||
// SpiderMonkey reports the JavaScript entry points occuring within this
|
||||
// AutoEntryMonitor's scope to the following member functions, which the
|
||||
// embedding is expected to override.
|
||||
//
|
||||
// It is important to note that |asyncCause| is owned by the caller and its
|
||||
// lifetime must outlive the lifetime of the AutoEntryMonitor object. It is
|
||||
// strongly encouraged that |asyncCause| be a string constant or similar
|
||||
// statically allocated string.
|
||||
|
||||
// We have begun executing |function|. Note that |function| may not be the
|
||||
// actual closure we are running, but only the canonical function object to
|
||||
// which the script refers.
|
||||
virtual void Entry(JSContext* cx, JSFunction* function,
|
||||
HandleValue asyncStack,
|
||||
const char* asyncCause) = 0;
|
||||
|
||||
// Execution has begun at the entry point of |script|, which is not a
|
||||
// function body. (This is probably being executed by 'eval' or some
|
||||
// JSAPI equivalent.)
|
||||
virtual void Entry(JSContext* cx, JSScript* script,
|
||||
HandleValue asyncStack,
|
||||
const char* asyncCause) = 0;
|
||||
|
||||
// Execution of the function or script has ended.
|
||||
virtual void Exit(JSContext* cx) { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace dbg
|
||||
} // namespace JS
|
||||
|
||||
|
||||
#endif /* js_Debug_h */
|
||||
|
|
@ -1,723 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_GCAPI_h
|
||||
#define js_GCAPI_h
|
||||
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
#include "js/GCAnnotations.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/UniquePtr.h"
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
class GCRuntime;
|
||||
} // namespace gc
|
||||
namespace gcstats {
|
||||
struct Statistics;
|
||||
} // namespace gcstats
|
||||
} // namespace js
|
||||
|
||||
typedef enum JSGCMode {
|
||||
/** Perform only global GCs. */
|
||||
JSGC_MODE_GLOBAL = 0,
|
||||
|
||||
/** Perform per-zone GCs until too much garbage has accumulated. */
|
||||
JSGC_MODE_ZONE = 1,
|
||||
|
||||
/**
|
||||
* Collect in short time slices rather than all at once. Implies
|
||||
* JSGC_MODE_ZONE.
|
||||
*/
|
||||
JSGC_MODE_INCREMENTAL = 2
|
||||
} JSGCMode;
|
||||
|
||||
/**
|
||||
* Kinds of js_GC invocation.
|
||||
*/
|
||||
typedef enum JSGCInvocationKind {
|
||||
/* Normal invocation. */
|
||||
GC_NORMAL = 0,
|
||||
|
||||
/* Minimize GC triggers and release empty GC chunks right away. */
|
||||
GC_SHRINK = 1
|
||||
} JSGCInvocationKind;
|
||||
|
||||
namespace JS {
|
||||
|
||||
#define GCREASONS(D) \
|
||||
/* Reasons internal to the JS engine */ \
|
||||
D(API) \
|
||||
D(EAGER_ALLOC_TRIGGER) \
|
||||
D(DESTROY_RUNTIME) \
|
||||
D(UNUSED0) \
|
||||
D(LAST_DITCH) \
|
||||
D(TOO_MUCH_MALLOC) \
|
||||
D(ALLOC_TRIGGER) \
|
||||
D(DEBUG_GC) \
|
||||
D(COMPARTMENT_REVIVED) \
|
||||
D(RESET) \
|
||||
D(OUT_OF_NURSERY) \
|
||||
D(EVICT_NURSERY) \
|
||||
D(FULL_STORE_BUFFER) \
|
||||
D(SHARED_MEMORY_LIMIT) \
|
||||
D(UNUSED1) \
|
||||
D(INCREMENTAL_TOO_SLOW) \
|
||||
D(ABORT_GC) \
|
||||
\
|
||||
/* These are reserved for future use. */ \
|
||||
D(RESERVED0) \
|
||||
D(RESERVED1) \
|
||||
D(RESERVED2) \
|
||||
D(RESERVED3) \
|
||||
D(RESERVED4) \
|
||||
D(RESERVED5) \
|
||||
D(RESERVED6) \
|
||||
D(RESERVED7) \
|
||||
D(RESERVED8) \
|
||||
D(RESERVED9) \
|
||||
D(RESERVED10) \
|
||||
D(RESERVED11) \
|
||||
D(RESERVED12) \
|
||||
D(RESERVED13) \
|
||||
D(RESERVED14) \
|
||||
D(RESERVED15) \
|
||||
\
|
||||
/* Reasons from Firefox */ \
|
||||
D(DOM_WINDOW_UTILS) \
|
||||
D(COMPONENT_UTILS) \
|
||||
D(MEM_PRESSURE) \
|
||||
D(CC_WAITING) \
|
||||
D(CC_FORCED) \
|
||||
D(LOAD_END) \
|
||||
D(POST_COMPARTMENT) \
|
||||
D(PAGE_HIDE) \
|
||||
D(NSJSCONTEXT_DESTROY) \
|
||||
D(SET_NEW_DOCUMENT) \
|
||||
D(SET_DOC_SHELL) \
|
||||
D(DOM_UTILS) \
|
||||
D(DOM_IPC) \
|
||||
D(DOM_WORKER) \
|
||||
D(INTER_SLICE_GC) \
|
||||
D(REFRESH_FRAME) \
|
||||
D(FULL_GC_TIMER) \
|
||||
D(SHUTDOWN_CC) \
|
||||
D(FINISH_LARGE_EVALUATE) \
|
||||
D(USER_INACTIVE) \
|
||||
D(XPCONNECT_SHUTDOWN)
|
||||
|
||||
namespace gcreason {
|
||||
|
||||
/* GCReasons will end up looking like JSGC_MAYBEGC */
|
||||
enum Reason {
|
||||
#define MAKE_REASON(name) name,
|
||||
GCREASONS(MAKE_REASON)
|
||||
#undef MAKE_REASON
|
||||
NO_REASON,
|
||||
NUM_REASONS,
|
||||
|
||||
/*
|
||||
* For telemetry, we want to keep a fixed max bucket size over time so we
|
||||
* don't have to switch histograms. 100 is conservative; as of this writing
|
||||
* there are 52. But the cost of extra buckets seems to be low while the
|
||||
* cost of switching histograms is high.
|
||||
*/
|
||||
NUM_TELEMETRY_REASONS = 100
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a statically allocated C string explaining the given GC reason.
|
||||
*/
|
||||
extern JS_PUBLIC_API(const char*)
|
||||
ExplainReason(JS::gcreason::Reason reason);
|
||||
|
||||
} /* namespace gcreason */
|
||||
|
||||
/*
|
||||
* Zone GC:
|
||||
*
|
||||
* SpiderMonkey's GC is capable of performing a collection on an arbitrary
|
||||
* subset of the zones in the system. This allows an embedding to minimize
|
||||
* collection time by only collecting zones that have run code recently,
|
||||
* ignoring the parts of the heap that are unlikely to have changed.
|
||||
*
|
||||
* When triggering a GC using one of the functions below, it is first necessary
|
||||
* to select the zones to be collected. To do this, you can call
|
||||
* PrepareZoneForGC on each zone, or you can call PrepareForFullGC to select
|
||||
* all zones. Failing to select any zone is an error.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Schedule the given zone to be collected as part of the next GC.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
PrepareZoneForGC(Zone* zone);
|
||||
|
||||
/**
|
||||
* Schedule all zones to be collected in the next GC.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
PrepareForFullGC(JSContext* cx);
|
||||
|
||||
/**
|
||||
* When performing an incremental GC, the zones that were selected for the
|
||||
* previous incremental slice must be selected in subsequent slices as well.
|
||||
* This function selects those slices automatically.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
PrepareForIncrementalGC(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Returns true if any zone in the system has been scheduled for GC with one of
|
||||
* the functions above or by the JS engine.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsGCScheduled(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Undoes the effect of the Prepare methods above. The given zone will not be
|
||||
* collected in the next GC.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SkipZoneForGC(Zone* zone);
|
||||
|
||||
/*
|
||||
* Non-Incremental GC:
|
||||
*
|
||||
* The following functions perform a non-incremental GC.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Performs a non-incremental collection of all selected zones.
|
||||
*
|
||||
* If the gckind argument is GC_NORMAL, then some objects that are unreachable
|
||||
* from the program may still be alive afterwards because of internal
|
||||
* references; if GC_SHRINK is passed then caches and other temporary references
|
||||
* to objects will be cleared and all unreferenced objects will be removed from
|
||||
* the system.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
GCForReason(JSContext* cx, JSGCInvocationKind gckind, gcreason::Reason reason);
|
||||
|
||||
/*
|
||||
* Incremental GC:
|
||||
*
|
||||
* Incremental GC divides the full mark-and-sweep collection into multiple
|
||||
* slices, allowing client JavaScript code to run between each slice. This
|
||||
* allows interactive apps to avoid long collection pauses. Incremental GC does
|
||||
* not make collection take less time, it merely spreads that time out so that
|
||||
* the pauses are less noticable.
|
||||
*
|
||||
* For a collection to be carried out incrementally the following conditions
|
||||
* must be met:
|
||||
* - The collection must be run by calling JS::IncrementalGC() rather than
|
||||
* JS_GC().
|
||||
* - The GC mode must have been set to JSGC_MODE_INCREMENTAL with
|
||||
* JS_SetGCParameter().
|
||||
*
|
||||
* Note: Even if incremental GC is enabled and working correctly,
|
||||
* non-incremental collections can still happen when low on memory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Begin an incremental collection and perform one slice worth of work. When
|
||||
* this function returns, the collection may not be complete.
|
||||
* IncrementalGCSlice() must be called repeatedly until
|
||||
* !IsIncrementalGCInProgress(cx).
|
||||
*
|
||||
* Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or
|
||||
* shorter than the requested interval.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
StartIncrementalGC(JSContext* cx, JSGCInvocationKind gckind, gcreason::Reason reason,
|
||||
int64_t millis = 0);
|
||||
|
||||
/**
|
||||
* Perform a slice of an ongoing incremental collection. When this function
|
||||
* returns, the collection may not be complete. It must be called repeatedly
|
||||
* until !IsIncrementalGCInProgress(cx).
|
||||
*
|
||||
* Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or
|
||||
* shorter than the requested interval.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
IncrementalGCSlice(JSContext* cx, gcreason::Reason reason, int64_t millis = 0);
|
||||
|
||||
/**
|
||||
* If IsIncrementalGCInProgress(cx), this call finishes the ongoing collection
|
||||
* by performing an arbitrarily long slice. If !IsIncrementalGCInProgress(cx),
|
||||
* this is equivalent to GCForReason. When this function returns,
|
||||
* IsIncrementalGCInProgress(cx) will always be false.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
FinishIncrementalGC(JSContext* cx, gcreason::Reason reason);
|
||||
|
||||
/**
|
||||
* If IsIncrementalGCInProgress(cx), this call aborts the ongoing collection and
|
||||
* performs whatever work needs to be done to return the collector to its idle
|
||||
* state. This may take an arbitrarily long time. When this function returns,
|
||||
* IsIncrementalGCInProgress(cx) will always be false.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
AbortIncrementalGC(JSContext* cx);
|
||||
|
||||
namespace dbg {
|
||||
|
||||
// The `JS::dbg::GarbageCollectionEvent` class is essentially a view of the
|
||||
// `js::gcstats::Statistics` data without the uber implementation-specific bits.
|
||||
// It should generally be palatable for web developers.
|
||||
class GarbageCollectionEvent
|
||||
{
|
||||
// The major GC number of the GC cycle this data pertains to.
|
||||
uint64_t majorGCNumber_;
|
||||
|
||||
// Reference to a non-owned, statically allocated C string. This is a very
|
||||
// short reason explaining why a GC was triggered.
|
||||
const char* reason;
|
||||
|
||||
// Reference to a nullable, non-owned, statically allocated C string. If the
|
||||
// collection was forced to be non-incremental, this is a short reason of
|
||||
// why the GC could not perform an incremental collection.
|
||||
const char* nonincrementalReason;
|
||||
|
||||
// Represents a single slice of a possibly multi-slice incremental garbage
|
||||
// collection.
|
||||
struct Collection {
|
||||
double startTimestamp;
|
||||
double endTimestamp;
|
||||
};
|
||||
|
||||
// The set of garbage collection slices that made up this GC cycle.
|
||||
mozilla::Vector<Collection> collections;
|
||||
|
||||
GarbageCollectionEvent(const GarbageCollectionEvent& rhs) = delete;
|
||||
GarbageCollectionEvent& operator=(const GarbageCollectionEvent& rhs) = delete;
|
||||
|
||||
public:
|
||||
explicit GarbageCollectionEvent(uint64_t majorGCNum)
|
||||
: majorGCNumber_(majorGCNum)
|
||||
, reason(nullptr)
|
||||
, nonincrementalReason(nullptr)
|
||||
, collections()
|
||||
{ }
|
||||
|
||||
using Ptr = js::UniquePtr<GarbageCollectionEvent>;
|
||||
static Ptr Create(JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t majorGCNumber);
|
||||
|
||||
JSObject* toJSObject(JSContext* cx) const;
|
||||
|
||||
uint64_t majorGCNumber() const { return majorGCNumber_; }
|
||||
};
|
||||
|
||||
} // namespace dbg
|
||||
|
||||
enum GCProgress {
|
||||
/*
|
||||
* During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END
|
||||
* callbacks. During an incremental GC, the sequence of callbacks is as
|
||||
* follows:
|
||||
* JSGC_CYCLE_BEGIN, JSGC_SLICE_END (first slice)
|
||||
* JSGC_SLICE_BEGIN, JSGC_SLICE_END (second slice)
|
||||
* ...
|
||||
* JSGC_SLICE_BEGIN, JSGC_CYCLE_END (last slice)
|
||||
*/
|
||||
|
||||
GC_CYCLE_BEGIN,
|
||||
GC_SLICE_BEGIN,
|
||||
GC_SLICE_END,
|
||||
GC_CYCLE_END
|
||||
};
|
||||
|
||||
struct JS_PUBLIC_API(GCDescription) {
|
||||
bool isZone_;
|
||||
JSGCInvocationKind invocationKind_;
|
||||
gcreason::Reason reason_;
|
||||
|
||||
GCDescription(bool isZone, JSGCInvocationKind kind, gcreason::Reason reason)
|
||||
: isZone_(isZone), invocationKind_(kind), reason_(reason) {}
|
||||
|
||||
char16_t* formatSliceMessage(JSContext* cx) const;
|
||||
char16_t* formatSummaryMessage(JSContext* cx) const;
|
||||
char16_t* formatJSON(JSContext* cx, uint64_t timestamp) const;
|
||||
|
||||
JS::dbg::GarbageCollectionEvent::Ptr toGCEvent(JSContext* cx) const;
|
||||
};
|
||||
|
||||
typedef void
|
||||
(* GCSliceCallback)(JSContext* cx, GCProgress progress, const GCDescription& desc);
|
||||
|
||||
/**
|
||||
* The GC slice callback is called at the beginning and end of each slice. This
|
||||
* callback may be used for GC notifications as well as to perform additional
|
||||
* marking.
|
||||
*/
|
||||
extern JS_PUBLIC_API(GCSliceCallback)
|
||||
SetGCSliceCallback(JSContext* cx, GCSliceCallback callback);
|
||||
|
||||
/**
|
||||
* Describes the progress of an observed nursery collection.
|
||||
*/
|
||||
enum class GCNurseryProgress {
|
||||
/**
|
||||
* The nursery collection is starting.
|
||||
*/
|
||||
GC_NURSERY_COLLECTION_START,
|
||||
/**
|
||||
* The nursery collection is ending.
|
||||
*/
|
||||
GC_NURSERY_COLLECTION_END
|
||||
};
|
||||
|
||||
/**
|
||||
* A nursery collection callback receives the progress of the nursery collection
|
||||
* and the reason for the collection.
|
||||
*/
|
||||
using GCNurseryCollectionCallback = void(*)(JSContext* cx, GCNurseryProgress progress,
|
||||
gcreason::Reason reason);
|
||||
|
||||
/**
|
||||
* Set the nursery collection callback for the given runtime. When set, it will
|
||||
* be called at the start and end of every nursery collection.
|
||||
*/
|
||||
extern JS_PUBLIC_API(GCNurseryCollectionCallback)
|
||||
SetGCNurseryCollectionCallback(JSContext* cx, GCNurseryCollectionCallback callback);
|
||||
|
||||
typedef void
|
||||
(* DoCycleCollectionCallback)(JSContext* cx);
|
||||
|
||||
/**
|
||||
* The purge gray callback is called after any COMPARTMENT_REVIVED GC in which
|
||||
* the majority of compartments have been marked gray.
|
||||
*/
|
||||
extern JS_PUBLIC_API(DoCycleCollectionCallback)
|
||||
SetDoCycleCollectionCallback(JSContext* cx, DoCycleCollectionCallback callback);
|
||||
|
||||
/**
|
||||
* Incremental GC defaults to enabled, but may be disabled for testing or in
|
||||
* embeddings that have not yet implemented barriers on their native classes.
|
||||
* There is not currently a way to re-enable incremental GC once it has been
|
||||
* disabled on the runtime.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
DisableIncrementalGC(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Returns true if incremental GC is enabled. Simply having incremental GC
|
||||
* enabled is not sufficient to ensure incremental collections are happening.
|
||||
* See the comment "Incremental GC" above for reasons why incremental GC may be
|
||||
* suppressed. Inspection of the "nonincremental reason" field of the
|
||||
* GCDescription returned by GCSliceCallback may help narrow down the cause if
|
||||
* collections are not happening incrementally when expected.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsIncrementalGCEnabled(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Returns true while an incremental GC is ongoing, both when actively
|
||||
* collecting and between slices.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsIncrementalGCInProgress(JSContext* cx);
|
||||
|
||||
/*
|
||||
* Returns true when writes to GC things must call an incremental (pre) barrier.
|
||||
* This is generally only true when running mutator code in-between GC slices.
|
||||
* At other times, the barrier may be elided for performance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsIncrementalBarrierNeeded(JSContext* cx);
|
||||
|
||||
/*
|
||||
* Notify the GC that a reference to a GC thing is about to be overwritten.
|
||||
* These methods must be called if IsIncrementalBarrierNeeded.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
IncrementalReferenceBarrier(GCCellPtr thing);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
IncrementalValueBarrier(const Value& v);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
IncrementalObjectBarrier(JSObject* obj);
|
||||
|
||||
/**
|
||||
* Returns true if the most recent GC ran incrementally.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
WasIncrementalGC(JSContext* cx);
|
||||
|
||||
/*
|
||||
* Generational GC:
|
||||
*
|
||||
* Note: Generational GC is not yet enabled by default. The following class
|
||||
* is non-functional unless SpiderMonkey was configured with
|
||||
* --enable-gcgenerational.
|
||||
*/
|
||||
|
||||
/** Ensure that generational GC is disabled within some scope. */
|
||||
class JS_PUBLIC_API(AutoDisableGenerationalGC)
|
||||
{
|
||||
js::gc::GCRuntime* gc;
|
||||
|
||||
public:
|
||||
explicit AutoDisableGenerationalGC(JSRuntime* rt);
|
||||
~AutoDisableGenerationalGC();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if generational allocation and collection is currently enabled
|
||||
* on the given runtime.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsGenerationalGCEnabled(JSRuntime* rt);
|
||||
|
||||
/**
|
||||
* Returns the GC's "number". This does not correspond directly to the number
|
||||
* of GCs that have been run, but is guaranteed to be monotonically increasing
|
||||
* with GC activity.
|
||||
*/
|
||||
extern JS_PUBLIC_API(size_t)
|
||||
GetGCNumber();
|
||||
|
||||
/**
|
||||
* Pass a subclass of this "abstract" class to callees to require that they
|
||||
* never GC. Subclasses can use assertions or the hazard analysis to ensure no
|
||||
* GC happens.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoRequireNoGC)
|
||||
{
|
||||
protected:
|
||||
AutoRequireNoGC() {}
|
||||
~AutoRequireNoGC() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Diagnostic assert (see MOZ_DIAGNOSTIC_ASSERT) that GC cannot occur while this
|
||||
* class is live. This class does not disable the static rooting hazard
|
||||
* analysis.
|
||||
*
|
||||
* This works by entering a GC unsafe region, which is checked on allocation and
|
||||
* on GC.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoAssertNoGC) : public AutoRequireNoGC
|
||||
{
|
||||
js::gc::GCRuntime* gc;
|
||||
size_t gcNumber;
|
||||
|
||||
public:
|
||||
AutoAssertNoGC();
|
||||
explicit AutoAssertNoGC(JSRuntime* rt);
|
||||
explicit AutoAssertNoGC(JSContext* cx);
|
||||
~AutoAssertNoGC();
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert if an allocation of a GC thing occurs while this class is live. This
|
||||
* class does not disable the static rooting hazard analysis.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoAssertNoAlloc)
|
||||
{
|
||||
#ifdef JS_DEBUG
|
||||
js::gc::GCRuntime* gc;
|
||||
|
||||
public:
|
||||
AutoAssertNoAlloc() : gc(nullptr) {}
|
||||
explicit AutoAssertNoAlloc(JSContext* cx);
|
||||
void disallowAlloc(JSRuntime* rt);
|
||||
~AutoAssertNoAlloc();
|
||||
#else
|
||||
public:
|
||||
AutoAssertNoAlloc() {}
|
||||
explicit AutoAssertNoAlloc(JSContext* cx) {}
|
||||
void disallowAlloc(JSRuntime* rt) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert if a GC barrier is invoked while this class is live. This class does
|
||||
* not disable the static rooting hazard analysis.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoAssertOnBarrier)
|
||||
{
|
||||
JSContext* context;
|
||||
bool prev;
|
||||
|
||||
public:
|
||||
explicit AutoAssertOnBarrier(JSContext* cx);
|
||||
~AutoAssertOnBarrier();
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable the static rooting hazard analysis in the live region and assert if
|
||||
* any allocation that could potentially trigger a GC occurs while this guard
|
||||
* object is live. This is most useful to help the exact rooting hazard analysis
|
||||
* in complex regions, since it cannot understand dataflow.
|
||||
*
|
||||
* Note: GC behavior is unpredictable even when deterministic and is generally
|
||||
* non-deterministic in practice. The fact that this guard has not
|
||||
* asserted is not a guarantee that a GC cannot happen in the guarded
|
||||
* region. As a rule, anyone performing a GC unsafe action should
|
||||
* understand the GC properties of all code in that region and ensure
|
||||
* that the hazard analysis is correct for that code, rather than relying
|
||||
* on this class.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc
|
||||
{
|
||||
public:
|
||||
AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {}
|
||||
explicit AutoSuppressGCAnalysis(JSContext* cx) : AutoAssertNoAlloc(cx) {}
|
||||
} JS_HAZ_GC_SUPPRESSED;
|
||||
|
||||
/**
|
||||
* Assert that code is only ever called from a GC callback, disable the static
|
||||
* rooting hazard analysis and assert if any allocation that could potentially
|
||||
* trigger a GC occurs while this guard object is live.
|
||||
*
|
||||
* This is useful to make the static analysis ignore code that runs in GC
|
||||
* callbacks.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoAssertGCCallback) : public AutoSuppressGCAnalysis
|
||||
{
|
||||
public:
|
||||
explicit AutoAssertGCCallback(JSObject* obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Place AutoCheckCannotGC in scopes that you believe can never GC. These
|
||||
* annotations will be verified both dynamically via AutoAssertNoGC, and
|
||||
* statically with the rooting hazard analysis (implemented by making the
|
||||
* analysis consider AutoCheckCannotGC to be a GC pointer, and therefore
|
||||
* complain if it is live across a GC call.) It is useful when dealing with
|
||||
* internal pointers to GC things where the GC thing itself may not be present
|
||||
* for the static analysis: e.g. acquiring inline chars from a JSString* on the
|
||||
* heap.
|
||||
*
|
||||
* We only do the assertion checking in DEBUG builds.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoAssertNoGC
|
||||
{
|
||||
public:
|
||||
AutoCheckCannotGC() : AutoAssertNoGC() {}
|
||||
explicit AutoCheckCannotGC(JSContext* cx) : AutoAssertNoGC(cx) {}
|
||||
} JS_HAZ_GC_INVALIDATED;
|
||||
#else
|
||||
class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoRequireNoGC
|
||||
{
|
||||
public:
|
||||
AutoCheckCannotGC() {}
|
||||
explicit AutoCheckCannotGC(JSContext* cx) {}
|
||||
} JS_HAZ_GC_INVALIDATED;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Unsets the gray bit for anything reachable from |thing|. |kind| should not be
|
||||
* JS::TraceKind::Shape. |thing| should be non-null. The return value indicates
|
||||
* if anything was unmarked.
|
||||
*/
|
||||
extern JS_FRIEND_API(bool)
|
||||
UnmarkGrayGCThingRecursively(GCCellPtr thing);
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
ExposeGCThingToActiveJS(JS::GCCellPtr thing)
|
||||
{
|
||||
// GC things residing in the nursery cannot be gray: they have no mark bits.
|
||||
// All live objects in the nursery are moved to tenured at the beginning of
|
||||
// each GC slice, so the gray marker never sees nursery things.
|
||||
if (IsInsideNursery(thing.asCell()))
|
||||
return;
|
||||
|
||||
// There's nothing to do for permanent GC things that might be owned by
|
||||
// another runtime.
|
||||
if (thing.mayBeOwnedByOtherRuntime())
|
||||
return;
|
||||
|
||||
JS::shadow::Runtime* rt = detail::GetCellRuntime(thing.asCell());
|
||||
MOZ_DIAGNOSTIC_ASSERT(rt->allowGCBarriers());
|
||||
|
||||
if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
|
||||
JS::IncrementalReferenceBarrier(thing);
|
||||
else if (!thing.mayBeOwnedByOtherRuntime() && js::gc::detail::CellIsMarkedGray(thing.asCell()))
|
||||
JS::UnmarkGrayGCThingRecursively(thing);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
MarkGCThingAsLive(JSRuntime* aRt, JS::GCCellPtr thing)
|
||||
{
|
||||
// Any object in the nursery will not be freed during any GC running at that
|
||||
// time.
|
||||
if (IsInsideNursery(thing.asCell()))
|
||||
return;
|
||||
|
||||
// There's nothing to do for permanent GC things that might be owned by
|
||||
// another runtime.
|
||||
if (thing.mayBeOwnedByOtherRuntime())
|
||||
return;
|
||||
|
||||
JS::shadow::Runtime* rt = JS::shadow::Runtime::asShadowRuntime(aRt);
|
||||
MOZ_DIAGNOSTIC_ASSERT(rt->allowGCBarriers());
|
||||
|
||||
if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
|
||||
JS::IncrementalReferenceBarrier(thing);
|
||||
}
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
|
||||
/*
|
||||
* This should be called when an object that is marked gray is exposed to the JS
|
||||
* engine (by handing it to running JS code or writing it into live JS
|
||||
* data). During incremental GC, since the gray bits haven't been computed yet,
|
||||
* we conservatively mark the object black.
|
||||
*/
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
ExposeObjectToActiveJS(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(obj);
|
||||
js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj));
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
ExposeScriptToActiveJS(JSScript* script)
|
||||
{
|
||||
js::gc::ExposeGCThingToActiveJS(GCCellPtr(script));
|
||||
}
|
||||
|
||||
/*
|
||||
* If a GC is currently marking, mark the string black.
|
||||
*/
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
MarkStringAsLive(Zone* zone, JSString* string)
|
||||
{
|
||||
JSRuntime* rt = JS::shadow::Zone::asShadowZone(zone)->runtimeFromMainThread();
|
||||
js::gc::MarkGCThingAsLive(rt, GCCellPtr(string));
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal to Firefox.
|
||||
*
|
||||
* Note: this is not related to the PokeGC in nsJSEnvironment.
|
||||
*/
|
||||
extern JS_FRIEND_API(void)
|
||||
PokeGC(JSContext* cx);
|
||||
|
||||
/*
|
||||
* Internal to Firefox.
|
||||
*/
|
||||
extern JS_FRIEND_API(void)
|
||||
NotifyDidPaint(JSContext* cx);
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
#endif /* js_GCAPI_h */
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_GCAnnotations_h
|
||||
#define js_GCAnnotations_h
|
||||
|
||||
// Set of annotations for the rooting hazard analysis, used to categorize types
|
||||
// and functions.
|
||||
#ifdef XGILL_PLUGIN
|
||||
|
||||
// Mark a type as being a GC thing (eg js::gc::Cell has this annotation).
|
||||
# define JS_HAZ_GC_THING __attribute__((tag("GC Thing")))
|
||||
|
||||
// Mark a type as holding a pointer to a GC thing (eg JS::Value has this
|
||||
// annotation.)
|
||||
# define JS_HAZ_GC_POINTER __attribute__((tag("GC Pointer")))
|
||||
|
||||
// Mark a type as a rooted pointer, suitable for use on the stack (eg all
|
||||
// Rooted<T> instantiations should have this.)
|
||||
# define JS_HAZ_ROOTED __attribute__((tag("Rooted Pointer")))
|
||||
|
||||
// Mark a type as something that should not be held live across a GC, but which
|
||||
// is not itself a GC pointer.
|
||||
# define JS_HAZ_GC_INVALIDATED __attribute__((tag("Invalidated by GC")))
|
||||
|
||||
// Mark a type that would otherwise be considered a GC Pointer (eg because it
|
||||
// contains a JS::Value field) as a non-GC pointer. It is handled almost the
|
||||
// same in the analysis as a rooted pointer, except it will not be reported as
|
||||
// an unnecessary root if used across a GC call. This should rarely be used,
|
||||
// but makes sense for something like ErrorResult, which only contains a GC
|
||||
// pointer when it holds an exception (and it does its own rooting,
|
||||
// conditionally.)
|
||||
# define JS_HAZ_NON_GC_POINTER __attribute__((tag("Suppressed GC Pointer")))
|
||||
|
||||
// Mark a function as something that runs a garbage collection, potentially
|
||||
// invalidating GC pointers.
|
||||
# define JS_HAZ_GC_CALL __attribute__((tag("GC Call")))
|
||||
|
||||
// Mark an RAII class as suppressing GC within its scope.
|
||||
# define JS_HAZ_GC_SUPPRESSED __attribute__((tag("Suppress GC")))
|
||||
|
||||
#else
|
||||
|
||||
# define JS_HAZ_GC_THING
|
||||
# define JS_HAZ_GC_POINTER
|
||||
# define JS_HAZ_ROOTED
|
||||
# define JS_HAZ_GC_INVALIDATED
|
||||
# define JS_HAZ_NON_GC_POINTER
|
||||
# define JS_HAZ_GC_CALL
|
||||
# define JS_HAZ_GC_SUPPRESSED
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* js_GCAnnotations_h */
|
||||
|
|
@ -1,399 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef GCHashTable_h
|
||||
#define GCHashTable_h
|
||||
|
||||
#include "js/GCPolicyAPI.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/SweepingAPI.h"
|
||||
#include "js/TracingAPI.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
// Define a reasonable default GC policy for GC-aware Maps.
|
||||
template <typename Key, typename Value>
|
||||
struct DefaultMapSweepPolicy {
|
||||
static bool needsSweep(Key* key, Value* value) {
|
||||
return GCPolicy<Key>::needsSweep(key) || GCPolicy<Value>::needsSweep(value);
|
||||
}
|
||||
};
|
||||
|
||||
// A GCHashMap is a GC-aware HashMap, meaning that it has additional trace and
|
||||
// sweep methods that know how to visit all keys and values in the table.
|
||||
// HashMaps that contain GC pointers will generally want to use this GCHashMap
|
||||
// specialization instead of HashMap, because this conveniently supports tracing
|
||||
// keys and values, and cleaning up weak entries.
|
||||
//
|
||||
// GCHashMap::trace applies GCPolicy<T>::trace to each entry's key and value.
|
||||
// Most types of GC pointers already have appropriate specializations of
|
||||
// GCPolicy, so they should just work as keys and values. Any struct type with a
|
||||
// default constructor and trace and sweep functions should work as well. If you
|
||||
// need to define your own GCPolicy specialization, generic helpers can be found
|
||||
// in js/public/TracingAPI.h.
|
||||
//
|
||||
// The MapSweepPolicy template parameter controls how the table drops entries
|
||||
// when swept. GCHashMap::sweep applies MapSweepPolicy::needsSweep to each table
|
||||
// entry; if it returns true, the entry is dropped. The default MapSweepPolicy
|
||||
// drops the entry if either the key or value is about to be finalized,
|
||||
// according to its GCPolicy<T>::needsSweep method. (This default is almost
|
||||
// always fine: it's hard to imagine keeping such an entry around anyway.)
|
||||
//
|
||||
// Note that this HashMap only knows *how* to trace and sweep, but it does not
|
||||
// itself cause tracing or sweeping to be invoked. For tracing, it must be used
|
||||
// with Rooted or PersistentRooted, or barriered and traced manually. For
|
||||
// sweeping, currently it requires an explicit call to <map>.sweep().
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename HashPolicy = js::DefaultHasher<Key>,
|
||||
typename AllocPolicy = js::TempAllocPolicy,
|
||||
typename MapSweepPolicy = DefaultMapSweepPolicy<Key, Value>>
|
||||
class GCHashMap : public js::HashMap<Key, Value, HashPolicy, AllocPolicy>
|
||||
{
|
||||
using Base = js::HashMap<Key, Value, HashPolicy, AllocPolicy>;
|
||||
|
||||
public:
|
||||
explicit GCHashMap(AllocPolicy a = AllocPolicy()) : Base(a) {}
|
||||
|
||||
static void trace(GCHashMap* map, JSTracer* trc) { map->trace(trc); }
|
||||
void trace(JSTracer* trc) {
|
||||
if (!this->initialized())
|
||||
return;
|
||||
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
|
||||
GCPolicy<Value>::trace(trc, &e.front().value(), "hashmap value");
|
||||
GCPolicy<Key>::trace(trc, &e.front().mutableKey(), "hashmap key");
|
||||
}
|
||||
}
|
||||
|
||||
void sweep() {
|
||||
if (!this->initialized())
|
||||
return;
|
||||
|
||||
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
|
||||
if (MapSweepPolicy::needsSweep(&e.front().mutableKey(), &e.front().value()))
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
|
||||
// GCHashMap is movable
|
||||
GCHashMap(GCHashMap&& rhs) : Base(mozilla::Move(rhs)) {}
|
||||
void operator=(GCHashMap&& rhs) {
|
||||
MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
|
||||
Base::operator=(mozilla::Move(rhs));
|
||||
}
|
||||
|
||||
private:
|
||||
// GCHashMap is not copyable or assignable
|
||||
GCHashMap(const GCHashMap& hm) = delete;
|
||||
GCHashMap& operator=(const GCHashMap& hm) = delete;
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
// HashMap that supports rekeying.
|
||||
//
|
||||
// If your keys are pointers to something like JSObject that can be tenured or
|
||||
// compacted, prefer to use GCHashMap with MovableCellHasher, which takes
|
||||
// advantage of the Zone's stable id table to make rekeying unnecessary.
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename HashPolicy = DefaultHasher<Key>,
|
||||
typename AllocPolicy = TempAllocPolicy,
|
||||
typename MapSweepPolicy = JS::DefaultMapSweepPolicy<Key, Value>>
|
||||
class GCRekeyableHashMap : public JS::GCHashMap<Key, Value, HashPolicy, AllocPolicy, MapSweepPolicy>
|
||||
{
|
||||
using Base = JS::GCHashMap<Key, Value, HashPolicy, AllocPolicy>;
|
||||
|
||||
public:
|
||||
explicit GCRekeyableHashMap(AllocPolicy a = AllocPolicy()) : Base(a) {}
|
||||
|
||||
void sweep() {
|
||||
if (!this->initialized())
|
||||
return;
|
||||
|
||||
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
|
||||
Key key(e.front().key());
|
||||
if (MapSweepPolicy::needsSweep(&key, &e.front().value()))
|
||||
e.removeFront();
|
||||
else if (!HashPolicy::match(key, e.front().key()))
|
||||
e.rekeyFront(key);
|
||||
}
|
||||
}
|
||||
|
||||
// GCRekeyableHashMap is movable
|
||||
GCRekeyableHashMap(GCRekeyableHashMap&& rhs) : Base(mozilla::Move(rhs)) {}
|
||||
void operator=(GCRekeyableHashMap&& rhs) {
|
||||
MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
|
||||
Base::operator=(mozilla::Move(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Outer, typename... Args>
|
||||
class GCHashMapOperations
|
||||
{
|
||||
using Map = JS::GCHashMap<Args...>;
|
||||
using Lookup = typename Map::Lookup;
|
||||
|
||||
const Map& map() const { return static_cast<const Outer*>(this)->get(); }
|
||||
|
||||
public:
|
||||
using AddPtr = typename Map::AddPtr;
|
||||
using Ptr = typename Map::Ptr;
|
||||
using Range = typename Map::Range;
|
||||
|
||||
bool initialized() const { return map().initialized(); }
|
||||
Ptr lookup(const Lookup& l) const { return map().lookup(l); }
|
||||
AddPtr lookupForAdd(const Lookup& l) const { return map().lookupForAdd(l); }
|
||||
Range all() const { return map().all(); }
|
||||
bool empty() const { return map().empty(); }
|
||||
uint32_t count() const { return map().count(); }
|
||||
size_t capacity() const { return map().capacity(); }
|
||||
bool has(const Lookup& l) const { return map().lookup(l).found(); }
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return map().sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return mallocSizeOf(this) + map().sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Outer, typename... Args>
|
||||
class MutableGCHashMapOperations
|
||||
: public GCHashMapOperations<Outer, Args...>
|
||||
{
|
||||
using Map = JS::GCHashMap<Args...>;
|
||||
using Lookup = typename Map::Lookup;
|
||||
|
||||
Map& map() { return static_cast<Outer*>(this)->get(); }
|
||||
|
||||
public:
|
||||
using AddPtr = typename Map::AddPtr;
|
||||
struct Enum : public Map::Enum { explicit Enum(Outer& o) : Map::Enum(o.map()) {} };
|
||||
using Ptr = typename Map::Ptr;
|
||||
using Range = typename Map::Range;
|
||||
|
||||
bool init(uint32_t len = 16) { return map().init(len); }
|
||||
void clear() { map().clear(); }
|
||||
void finish() { map().finish(); }
|
||||
void remove(Ptr p) { map().remove(p); }
|
||||
|
||||
template<typename KeyInput, typename ValueInput>
|
||||
bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
|
||||
return map().add(p, mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
|
||||
}
|
||||
|
||||
template<typename KeyInput>
|
||||
bool add(AddPtr& p, KeyInput&& k) {
|
||||
return map().add(p, mozilla::Forward<KeyInput>(k), Map::Value());
|
||||
}
|
||||
|
||||
template<typename KeyInput, typename ValueInput>
|
||||
bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
|
||||
return map().relookupOrAdd(p, k,
|
||||
mozilla::Forward<KeyInput>(k),
|
||||
mozilla::Forward<ValueInput>(v));
|
||||
}
|
||||
|
||||
template<typename KeyInput, typename ValueInput>
|
||||
bool put(KeyInput&& k, ValueInput&& v) {
|
||||
return map().put(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
|
||||
}
|
||||
|
||||
template<typename KeyInput, typename ValueInput>
|
||||
bool putNew(KeyInput&& k, ValueInput&& v) {
|
||||
return map().putNew(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename A, typename B, typename C, typename D, typename E>
|
||||
class RootedBase<JS::GCHashMap<A,B,C,D,E>>
|
||||
: public MutableGCHashMapOperations<JS::Rooted<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
|
||||
{};
|
||||
|
||||
template <typename A, typename B, typename C, typename D, typename E>
|
||||
class MutableHandleBase<JS::GCHashMap<A,B,C,D,E>>
|
||||
: public MutableGCHashMapOperations<JS::MutableHandle<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
|
||||
{};
|
||||
|
||||
template <typename A, typename B, typename C, typename D, typename E>
|
||||
class HandleBase<JS::GCHashMap<A,B,C,D,E>>
|
||||
: public GCHashMapOperations<JS::Handle<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
|
||||
{};
|
||||
|
||||
template <typename A, typename B, typename C, typename D, typename E>
|
||||
class WeakCacheBase<JS::GCHashMap<A,B,C,D,E>>
|
||||
: public MutableGCHashMapOperations<JS::WeakCache<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
|
||||
{};
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
// A GCHashSet is a HashSet with an additional trace method that knows
|
||||
// be traced to be kept alive will generally want to use this GCHashSet
|
||||
// specialization in lieu of HashSet.
|
||||
//
|
||||
// Most types of GC pointers can be traced with no extra infrastructure. For
|
||||
// structs and non-gc-pointer members, ensure that there is a specialization of
|
||||
// GCPolicy<T> with an appropriate trace method available to handle the custom
|
||||
// type. Generic helpers can be found in js/public/TracingAPI.h.
|
||||
//
|
||||
// Note that although this HashSet's trace will deal correctly with moved
|
||||
// elements, it does not itself know when to barrier or trace elements. To
|
||||
// function properly it must either be used with Rooted or barriered and traced
|
||||
// manually.
|
||||
template <typename T,
|
||||
typename HashPolicy = js::DefaultHasher<T>,
|
||||
typename AllocPolicy = js::TempAllocPolicy>
|
||||
class GCHashSet : public js::HashSet<T, HashPolicy, AllocPolicy>
|
||||
{
|
||||
using Base = js::HashSet<T, HashPolicy, AllocPolicy>;
|
||||
|
||||
public:
|
||||
explicit GCHashSet(AllocPolicy a = AllocPolicy()) : Base(a) {}
|
||||
|
||||
static void trace(GCHashSet* set, JSTracer* trc) { set->trace(trc); }
|
||||
void trace(JSTracer* trc) {
|
||||
if (!this->initialized())
|
||||
return;
|
||||
for (typename Base::Enum e(*this); !e.empty(); e.popFront())
|
||||
GCPolicy<T>::trace(trc, &e.mutableFront(), "hashset element");
|
||||
}
|
||||
|
||||
void sweep() {
|
||||
if (!this->initialized())
|
||||
return;
|
||||
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
|
||||
if (GCPolicy<T>::needsSweep(&e.mutableFront()))
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
|
||||
// GCHashSet is movable
|
||||
GCHashSet(GCHashSet&& rhs) : Base(mozilla::Move(rhs)) {}
|
||||
void operator=(GCHashSet&& rhs) {
|
||||
MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
|
||||
Base::operator=(mozilla::Move(rhs));
|
||||
}
|
||||
|
||||
private:
|
||||
// GCHashSet is not copyable or assignable
|
||||
GCHashSet(const GCHashSet& hs) = delete;
|
||||
GCHashSet& operator=(const GCHashSet& hs) = delete;
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
template <typename Outer, typename... Args>
|
||||
class GCHashSetOperations
|
||||
{
|
||||
using Set = JS::GCHashSet<Args...>;
|
||||
using Lookup = typename Set::Lookup;
|
||||
|
||||
const Set& set() const { return static_cast<const Outer*>(this)->get(); }
|
||||
|
||||
public:
|
||||
using AddPtr = typename Set::AddPtr;
|
||||
using Entry = typename Set::Entry;
|
||||
using Ptr = typename Set::Ptr;
|
||||
using Range = typename Set::Range;
|
||||
|
||||
bool initialized() const { return set().initialized(); }
|
||||
Ptr lookup(const Lookup& l) const { return set().lookup(l); }
|
||||
AddPtr lookupForAdd(const Lookup& l) const { return set().lookupForAdd(l); }
|
||||
Range all() const { return set().all(); }
|
||||
bool empty() const { return set().empty(); }
|
||||
uint32_t count() const { return set().count(); }
|
||||
size_t capacity() const { return set().capacity(); }
|
||||
bool has(const Lookup& l) const { return set().lookup(l).found(); }
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return set().sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return mallocSizeOf(this) + set().sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Outer, typename... Args>
|
||||
class MutableGCHashSetOperations
|
||||
: public GCHashSetOperations<Outer, Args...>
|
||||
{
|
||||
using Set = JS::GCHashSet<Args...>;
|
||||
using Lookup = typename Set::Lookup;
|
||||
|
||||
Set& set() { return static_cast<Outer*>(this)->get(); }
|
||||
|
||||
public:
|
||||
using AddPtr = typename Set::AddPtr;
|
||||
using Entry = typename Set::Entry;
|
||||
struct Enum : public Set::Enum { explicit Enum(Outer& o) : Set::Enum(o.set()) {} };
|
||||
using Ptr = typename Set::Ptr;
|
||||
using Range = typename Set::Range;
|
||||
|
||||
bool init(uint32_t len = 16) { return set().init(len); }
|
||||
void clear() { set().clear(); }
|
||||
void finish() { set().finish(); }
|
||||
void remove(Ptr p) { set().remove(p); }
|
||||
void remove(const Lookup& l) { set().remove(l); }
|
||||
|
||||
template<typename TInput>
|
||||
bool add(AddPtr& p, TInput&& t) {
|
||||
return set().add(p, mozilla::Forward<TInput>(t));
|
||||
}
|
||||
|
||||
template<typename TInput>
|
||||
bool relookupOrAdd(AddPtr& p, const Lookup& l, TInput&& t) {
|
||||
return set().relookupOrAdd(p, l, mozilla::Forward<TInput>(t));
|
||||
}
|
||||
|
||||
template<typename TInput>
|
||||
bool put(TInput&& t) {
|
||||
return set().put(mozilla::Forward<TInput>(t));
|
||||
}
|
||||
|
||||
template<typename TInput>
|
||||
bool putNew(TInput&& t) {
|
||||
return set().putNew(mozilla::Forward<TInput>(t));
|
||||
}
|
||||
|
||||
template<typename TInput>
|
||||
bool putNew(const Lookup& l, TInput&& t) {
|
||||
return set().putNew(l, mozilla::Forward<TInput>(t));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename HP, typename AP>
|
||||
class RootedBase<JS::GCHashSet<T, HP, AP>>
|
||||
: public MutableGCHashSetOperations<JS::Rooted<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename HP, typename AP>
|
||||
class MutableHandleBase<JS::GCHashSet<T, HP, AP>>
|
||||
: public MutableGCHashSetOperations<JS::MutableHandle<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename HP, typename AP>
|
||||
class HandleBase<JS::GCHashSet<T, HP, AP>>
|
||||
: public GCHashSetOperations<JS::Handle<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename HP, typename AP>
|
||||
class WeakCacheBase<JS::GCHashSet<T, HP, AP>>
|
||||
: public MutableGCHashSetOperations<JS::WeakCache<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
|
||||
{
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* GCHashTable_h */
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
// GC Policy Mechanism
|
||||
|
||||
// A GCPolicy controls how the GC interacts with both direct pointers to GC
|
||||
// things (e.g. JSObject* or JSString*), tagged and/or optional pointers to GC
|
||||
// things (e.g. Value or jsid), and C++ container types (e.g.
|
||||
// JSPropertyDescriptor or GCHashMap).
|
||||
//
|
||||
// The GCPolicy provides at a minimum:
|
||||
//
|
||||
// static T initial()
|
||||
// - Construct and return an empty T.
|
||||
//
|
||||
// static void trace(JSTracer, T* tp, const char* name)
|
||||
// - Trace the edge |*tp|, calling the edge |name|. Containers like
|
||||
// GCHashMap and GCHashSet use this method to trace their children.
|
||||
//
|
||||
// static bool needsSweep(T* tp)
|
||||
// - Return true if |*tp| is about to be finalized. Otherwise, update the
|
||||
// edge for moving GC, and return false. Containers like GCHashMap and
|
||||
// GCHashSet use this method to decide when to remove an entry: if this
|
||||
// function returns true on a key/value/member/etc, its entry is dropped
|
||||
// from the container. Specializing this method is the standard way to
|
||||
// get custom weak behavior from a container type.
|
||||
//
|
||||
// The default GCPolicy<T> assumes that T has a default constructor and |trace|
|
||||
// and |needsSweep| methods, and forwards to them. GCPolicy has appropriate
|
||||
// specializations for pointers to GC things and pointer-like types like
|
||||
// JS::Heap<T> and mozilla::UniquePtr<T>.
|
||||
//
|
||||
// There are some stock structs your specializations can inherit from.
|
||||
// IgnoreGCPolicy<T> does nothing. StructGCPolicy<T> forwards the methods to the
|
||||
// referent type T.
|
||||
|
||||
#ifndef GCPolicyAPI_h
|
||||
#define GCPolicyAPI_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "js/TraceKind.h"
|
||||
#include "js/TracingAPI.h"
|
||||
|
||||
// Expand the given macro D for each public GC pointer.
|
||||
#define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
|
||||
D(JS::Symbol*) \
|
||||
D(JSAtom*) \
|
||||
D(JSFunction*) \
|
||||
D(JSObject*) \
|
||||
D(JSScript*) \
|
||||
D(JSString*)
|
||||
|
||||
// Expand the given macro D for each public tagged GC pointer type.
|
||||
#define FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
|
||||
D(JS::Value) \
|
||||
D(jsid)
|
||||
|
||||
#define FOR_EACH_PUBLIC_AGGREGATE_GC_POINTER_TYPE(D) \
|
||||
D(JSPropertyDescriptor)
|
||||
|
||||
class JSAtom;
|
||||
class JSFunction;
|
||||
class JSObject;
|
||||
class JSScript;
|
||||
class JSString;
|
||||
namespace JS {
|
||||
class Symbol;
|
||||
}
|
||||
|
||||
namespace JS {
|
||||
|
||||
// Defines a policy for container types with non-GC, i.e. C storage. This
|
||||
// policy dispatches to the underlying struct for GC interactions.
|
||||
template <typename T>
|
||||
struct StructGCPolicy
|
||||
{
|
||||
static T initial() {
|
||||
return T();
|
||||
}
|
||||
|
||||
static void trace(JSTracer* trc, T* tp, const char* name) {
|
||||
tp->trace(trc);
|
||||
}
|
||||
|
||||
static void sweep(T* tp) {
|
||||
return tp->sweep();
|
||||
}
|
||||
|
||||
static bool needsSweep(T* tp) {
|
||||
return tp->needsSweep();
|
||||
}
|
||||
};
|
||||
|
||||
// The default GC policy attempts to defer to methods on the underlying type.
|
||||
// Most C++ structures that contain a default constructor, a trace function and
|
||||
// a sweep function will work out of the box with Rooted, Handle, GCVector,
|
||||
// and GCHash{Set,Map}.
|
||||
template <typename T> struct GCPolicy : public StructGCPolicy<T> {};
|
||||
|
||||
// This policy ignores any GC interaction, e.g. for non-GC types.
|
||||
template <typename T>
|
||||
struct IgnoreGCPolicy {
|
||||
static T initial() { return T(); }
|
||||
static void trace(JSTracer* trc, T* t, const char* name) {}
|
||||
static bool needsSweep(T* v) { return false; }
|
||||
};
|
||||
template <> struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
|
||||
template <> struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
|
||||
|
||||
template <typename T>
|
||||
struct GCPointerPolicy
|
||||
{
|
||||
static T initial() { return nullptr; }
|
||||
static void trace(JSTracer* trc, T* vp, const char* name) {
|
||||
if (*vp)
|
||||
js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
|
||||
}
|
||||
static bool needsSweep(T* vp) {
|
||||
if (*vp)
|
||||
return js::gc::IsAboutToBeFinalizedUnbarriered(vp);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
template <> struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
|
||||
template <> struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
|
||||
template <> struct GCPolicy<JSFunction*> : public GCPointerPolicy<JSFunction*> {};
|
||||
template <> struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
|
||||
template <> struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
|
||||
template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
|
||||
|
||||
template <typename T>
|
||||
struct GCPolicy<JS::Heap<T>>
|
||||
{
|
||||
static void trace(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
|
||||
TraceEdge(trc, thingp, name);
|
||||
}
|
||||
static bool needsSweep(JS::Heap<T>* thingp) {
|
||||
return js::gc::EdgeNeedsSweep(thingp);
|
||||
}
|
||||
};
|
||||
|
||||
// GCPolicy<UniquePtr<T>> forwards the contained pointer to GCPolicy<T>.
|
||||
template <typename T, typename D>
|
||||
struct GCPolicy<mozilla::UniquePtr<T, D>>
|
||||
{
|
||||
static mozilla::UniquePtr<T,D> initial() { return mozilla::UniquePtr<T,D>(); }
|
||||
static void trace(JSTracer* trc, mozilla::UniquePtr<T,D>* tp, const char* name) {
|
||||
if (tp->get())
|
||||
GCPolicy<T>::trace(trc, tp->get(), name);
|
||||
}
|
||||
static bool needsSweep(mozilla::UniquePtr<T,D>* tp) {
|
||||
if (tp->get())
|
||||
return GCPolicy<T>::needsSweep(tp->get());
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // GCPolicyAPI_h
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_GCVariant_h
|
||||
#define js_GCVariant_h
|
||||
|
||||
#include "mozilla/Variant.h"
|
||||
|
||||
#include "js/GCPolicyAPI.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TracingAPI.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
// These template specializations allow Variant to be used inside GC wrappers.
|
||||
//
|
||||
// When matching on GC wrappers around Variants, matching should be done on
|
||||
// the wrapper itself. The matcher class's methods should take Handles or
|
||||
// MutableHandles. For example,
|
||||
//
|
||||
// struct MyMatcher
|
||||
// {
|
||||
// using ReturnType = const char*;
|
||||
// ReturnType match(HandleObject o) { return "object"; }
|
||||
// ReturnType match(HandleScript s) { return "script"; }
|
||||
// };
|
||||
//
|
||||
// Rooted<Variant<JSObject*, JSScript*>> v(cx, someScript);
|
||||
// MyMatcher mm;
|
||||
// v.match(mm);
|
||||
//
|
||||
// If you get compile errors about inability to upcast subclasses (e.g., from
|
||||
// NativeObject* to JSObject*) and are inside js/src, be sure to also include
|
||||
// "gc/Policy.h".
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename... Ts>
|
||||
struct GCVariantImplementation;
|
||||
|
||||
// The base case.
|
||||
template <typename T>
|
||||
struct GCVariantImplementation<T>
|
||||
{
|
||||
template <typename ConcreteVariant>
|
||||
static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) {
|
||||
T& thing = v->template as<T>();
|
||||
if (!mozilla::IsPointer<T>::value || thing)
|
||||
GCPolicy<T>::trace(trc, &thing, name);
|
||||
}
|
||||
|
||||
template <typename Matcher, typename ConcreteVariant>
|
||||
static typename Matcher::ReturnType
|
||||
match(Matcher& matcher, Handle<ConcreteVariant> v) {
|
||||
const T& thing = v.get().template as<T>();
|
||||
return matcher.match(Handle<T>::fromMarkedLocation(&thing));
|
||||
}
|
||||
|
||||
template <typename Matcher, typename ConcreteVariant>
|
||||
static typename Matcher::ReturnType
|
||||
match(Matcher& matcher, MutableHandle<ConcreteVariant> v) {
|
||||
T& thing = v.get().template as<T>();
|
||||
return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing));
|
||||
}
|
||||
};
|
||||
|
||||
// The inductive case.
|
||||
template <typename T, typename... Ts>
|
||||
struct GCVariantImplementation<T, Ts...>
|
||||
{
|
||||
using Next = GCVariantImplementation<Ts...>;
|
||||
|
||||
template <typename ConcreteVariant>
|
||||
static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) {
|
||||
if (v->template is<T>()) {
|
||||
T& thing = v->template as<T>();
|
||||
if (!mozilla::IsPointer<T>::value || thing)
|
||||
GCPolicy<T>::trace(trc, &thing, name);
|
||||
} else {
|
||||
Next::trace(trc, v, name);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Matcher, typename ConcreteVariant>
|
||||
static typename Matcher::ReturnType
|
||||
match(Matcher& matcher, Handle<ConcreteVariant> v) {
|
||||
if (v.get().template is<T>()) {
|
||||
const T& thing = v.get().template as<T>();
|
||||
return matcher.match(Handle<T>::fromMarkedLocation(&thing));
|
||||
}
|
||||
return Next::match(matcher, v);
|
||||
}
|
||||
|
||||
template <typename Matcher, typename ConcreteVariant>
|
||||
static typename Matcher::ReturnType
|
||||
match(Matcher& matcher, MutableHandle<ConcreteVariant> v) {
|
||||
if (v.get().template is<T>()) {
|
||||
T& thing = v.get().template as<T>();
|
||||
return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing));
|
||||
}
|
||||
return Next::match(matcher, v);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Ts>
|
||||
struct GCPolicy<mozilla::Variant<Ts...>>
|
||||
{
|
||||
using Impl = detail::GCVariantImplementation<Ts...>;
|
||||
|
||||
// Variants do not provide initial(). They do not have a default initial
|
||||
// value and one must be provided.
|
||||
|
||||
static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, const char* name) {
|
||||
Impl::trace(trc, v, name);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
template <typename Outer, typename... Ts>
|
||||
class GCVariantOperations
|
||||
{
|
||||
using Impl = JS::detail::GCVariantImplementation<Ts...>;
|
||||
using Variant = mozilla::Variant<Ts...>;
|
||||
|
||||
const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
return variant().template is<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
JS::Handle<T> as() const {
|
||||
return Handle<T>::fromMarkedLocation(&variant().template as<T>());
|
||||
}
|
||||
|
||||
template <typename Matcher>
|
||||
typename Matcher::ReturnType
|
||||
match(Matcher& matcher) const {
|
||||
return Impl::match(matcher, JS::Handle<Variant>::fromMarkedLocation(&variant()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Outer, typename... Ts>
|
||||
class MutableGCVariantOperations
|
||||
: public GCVariantOperations<Outer, Ts...>
|
||||
{
|
||||
using Impl = JS::detail::GCVariantImplementation<Ts...>;
|
||||
using Variant = mozilla::Variant<Ts...>;
|
||||
|
||||
const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
|
||||
Variant& variant() { return static_cast<Outer*>(this)->get(); }
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
JS::MutableHandle<T> as() {
|
||||
return JS::MutableHandle<T>::fromMarkedLocation(&variant().template as<T>());
|
||||
}
|
||||
|
||||
template <typename Matcher>
|
||||
typename Matcher::ReturnType
|
||||
match(Matcher& matcher) {
|
||||
return Impl::match(matcher, JS::MutableHandle<Variant>::fromMarkedLocation(&variant()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
class RootedBase<mozilla::Variant<Ts...>>
|
||||
: public MutableGCVariantOperations<JS::Rooted<mozilla::Variant<Ts...>>, Ts...>
|
||||
{ };
|
||||
|
||||
template <typename... Ts>
|
||||
class MutableHandleBase<mozilla::Variant<Ts...>>
|
||||
: public MutableGCVariantOperations<JS::MutableHandle<mozilla::Variant<Ts...>>, Ts...>
|
||||
{ };
|
||||
|
||||
template <typename... Ts>
|
||||
class HandleBase<mozilla::Variant<Ts...>>
|
||||
: public GCVariantOperations<JS::Handle<mozilla::Variant<Ts...>>, Ts...>
|
||||
{ };
|
||||
|
||||
template <typename... Ts>
|
||||
class PersistentRootedBase<mozilla::Variant<Ts...>>
|
||||
: public MutableGCVariantOperations<JS::PersistentRooted<mozilla::Variant<Ts...>>, Ts...>
|
||||
{ };
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // js_GCVariant_h
|
||||
|
|
@ -1,249 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_GCVector_h
|
||||
#define js_GCVector_h
|
||||
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
#include "js/GCPolicyAPI.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
// A GCVector is a Vector with an additional trace method that knows how
|
||||
// to visit all of the items stored in the Vector. For vectors that contain GC
|
||||
// things, this is usually more convenient than manually iterating and marking
|
||||
// the contents.
|
||||
//
|
||||
// Most types of GC pointers as keys and values can be traced with no extra
|
||||
// infrastructure. For structs and non-gc-pointer members, ensure that there is
|
||||
// a specialization of GCPolicy<T> with an appropriate trace method available
|
||||
// to handle the custom type. Generic helpers can be found in
|
||||
// js/public/TracingAPI.h.
|
||||
//
|
||||
// Note that although this Vector's trace will deal correctly with moved items,
|
||||
// it does not itself know when to barrier or trace items. To function properly
|
||||
// it must either be used with Rooted, or barriered and traced manually.
|
||||
template <typename T,
|
||||
size_t MinInlineCapacity = 0,
|
||||
typename AllocPolicy = js::TempAllocPolicy>
|
||||
class GCVector
|
||||
{
|
||||
mozilla::Vector<T, MinInlineCapacity, AllocPolicy> vector;
|
||||
|
||||
public:
|
||||
explicit GCVector(AllocPolicy alloc = AllocPolicy())
|
||||
: vector(alloc)
|
||||
{}
|
||||
|
||||
GCVector(GCVector&& vec)
|
||||
: vector(mozilla::Move(vec.vector))
|
||||
{}
|
||||
|
||||
GCVector& operator=(GCVector&& vec) {
|
||||
vector = mozilla::Move(vec.vector);
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t length() const { return vector.length(); }
|
||||
bool empty() const { return vector.empty(); }
|
||||
size_t capacity() const { return vector.capacity(); }
|
||||
|
||||
T* begin() { return vector.begin(); }
|
||||
const T* begin() const { return vector.begin(); }
|
||||
|
||||
T* end() { return vector.end(); }
|
||||
const T* end() const { return vector.end(); }
|
||||
|
||||
T& operator[](size_t i) { return vector[i]; }
|
||||
const T& operator[](size_t i) const { return vector[i]; }
|
||||
|
||||
T& back() { return vector.back(); }
|
||||
const T& back() const { return vector.back(); }
|
||||
|
||||
bool initCapacity(size_t cap) { return vector.initCapacity(cap); }
|
||||
bool reserve(size_t req) { return vector.reserve(req); }
|
||||
void shrinkBy(size_t amount) { return vector.shrinkBy(amount); }
|
||||
bool growBy(size_t amount) { return vector.growBy(amount); }
|
||||
bool resize(size_t newLen) { return vector.resize(newLen); }
|
||||
|
||||
void clear() { return vector.clear(); }
|
||||
|
||||
template<typename U> bool append(U&& item) { return vector.append(mozilla::Forward<U>(item)); }
|
||||
|
||||
template<typename... Args>
|
||||
bool
|
||||
emplaceBack(Args&&... args) {
|
||||
return vector.emplaceBack(mozilla::Forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void infallibleAppend(U&& aU) {
|
||||
return vector.infallibleAppend(mozilla::Forward<U>(aU));
|
||||
}
|
||||
void infallibleAppendN(const T& aT, size_t aN) {
|
||||
return vector.infallibleAppendN(aT, aN);
|
||||
}
|
||||
template<typename U> void
|
||||
infallibleAppend(const U* aBegin, const U* aEnd) {
|
||||
return vector.infallibleAppend(aBegin, aEnd);
|
||||
}
|
||||
template<typename U> void infallibleAppend(const U* aBegin, size_t aLength) {
|
||||
return vector.infallibleAppend(aBegin, aLength);
|
||||
}
|
||||
|
||||
template<typename U, size_t O, class BP>
|
||||
bool appendAll(const mozilla::Vector<U, O, BP>& aU) { return vector.appendAll(aU); }
|
||||
template<typename U, size_t O, class BP>
|
||||
bool appendAll(const GCVector<U, O, BP>& aU) { return vector.append(aU.begin(), aU.length()); }
|
||||
|
||||
bool appendN(const T& val, size_t count) { return vector.appendN(val, count); }
|
||||
|
||||
template<typename U> bool append(const U* aBegin, const U* aEnd) {
|
||||
return vector.append(aBegin, aEnd);
|
||||
}
|
||||
template<typename U> bool append(const U* aBegin, size_t aLength) {
|
||||
return vector.append(aBegin, aLength);
|
||||
}
|
||||
|
||||
void popBack() { return vector.popBack(); }
|
||||
T popCopy() { return vector.popCopy(); }
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return vector.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return vector.sizeOfIncludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
static void trace(GCVector* vec, JSTracer* trc) { vec->trace(trc); }
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
for (auto& elem : vector)
|
||||
GCPolicy<T>::trace(trc, &elem, "vector element");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
|
||||
class GCVectorOperations
|
||||
{
|
||||
using Vec = JS::GCVector<T, Capacity, AllocPolicy>;
|
||||
const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
|
||||
|
||||
public:
|
||||
const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
|
||||
size_t length() const { return vec().length(); }
|
||||
bool empty() const { return vec().empty(); }
|
||||
size_t capacity() const { return vec().capacity(); }
|
||||
const T* begin() const { return vec().begin(); }
|
||||
const T* end() const { return vec().end(); }
|
||||
const T& back() const { return vec().back(); }
|
||||
|
||||
JS::Handle<T> operator[](size_t aIndex) const {
|
||||
return JS::Handle<T>::fromMarkedLocation(&vec().operator[](aIndex));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
|
||||
class MutableGCVectorOperations
|
||||
: public GCVectorOperations<Outer, T, Capacity, AllocPolicy>
|
||||
{
|
||||
using Vec = JS::GCVector<T, Capacity, AllocPolicy>;
|
||||
const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
|
||||
Vec& vec() { return static_cast<Outer*>(this)->get(); }
|
||||
|
||||
public:
|
||||
const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
|
||||
AllocPolicy& allocPolicy() { return vec().allocPolicy(); }
|
||||
const T* begin() const { return vec().begin(); }
|
||||
T* begin() { return vec().begin(); }
|
||||
const T* end() const { return vec().end(); }
|
||||
T* end() { return vec().end(); }
|
||||
const T& back() const { return vec().back(); }
|
||||
T& back() { return vec().back(); }
|
||||
|
||||
JS::Handle<T> operator[](size_t aIndex) const {
|
||||
return JS::Handle<T>::fromMarkedLocation(&vec().operator[](aIndex));
|
||||
}
|
||||
JS::MutableHandle<T> operator[](size_t aIndex) {
|
||||
return JS::MutableHandle<T>::fromMarkedLocation(&vec().operator[](aIndex));
|
||||
}
|
||||
|
||||
bool initCapacity(size_t aRequest) { return vec().initCapacity(aRequest); }
|
||||
bool reserve(size_t aRequest) { return vec().reserve(aRequest); }
|
||||
void shrinkBy(size_t aIncr) { vec().shrinkBy(aIncr); }
|
||||
bool growBy(size_t aIncr) { return vec().growBy(aIncr); }
|
||||
bool resize(size_t aNewLength) { return vec().resize(aNewLength); }
|
||||
bool growByUninitialized(size_t aIncr) { return vec().growByUninitialized(aIncr); }
|
||||
void infallibleGrowByUninitialized(size_t aIncr) { vec().infallibleGrowByUninitialized(aIncr); }
|
||||
bool resizeUninitialized(size_t aNewLength) { return vec().resizeUninitialized(aNewLength); }
|
||||
void clear() { vec().clear(); }
|
||||
void clearAndFree() { vec().clearAndFree(); }
|
||||
template<typename U> bool append(U&& aU) { return vec().append(mozilla::Forward<U>(aU)); }
|
||||
template<typename... Args> bool emplaceBack(Args&&... aArgs) {
|
||||
return vec().emplaceBack(mozilla::Forward<Args...>(aArgs...));
|
||||
}
|
||||
template<typename U, size_t O, class BP>
|
||||
bool appendAll(const mozilla::Vector<U, O, BP>& aU) { return vec().appendAll(aU); }
|
||||
template<typename U, size_t O, class BP>
|
||||
bool appendAll(const JS::GCVector<U, O, BP>& aU) { return vec().appendAll(aU); }
|
||||
bool appendN(const T& aT, size_t aN) { return vec().appendN(aT, aN); }
|
||||
template<typename U> bool append(const U* aBegin, const U* aEnd) {
|
||||
return vec().append(aBegin, aEnd);
|
||||
}
|
||||
template<typename U> bool append(const U* aBegin, size_t aLength) {
|
||||
return vec().append(aBegin, aLength);
|
||||
}
|
||||
template<typename U> void infallibleAppend(U&& aU) {
|
||||
vec().infallibleAppend(mozilla::Forward<U>(aU));
|
||||
}
|
||||
void infallibleAppendN(const T& aT, size_t aN) { vec().infallibleAppendN(aT, aN); }
|
||||
template<typename U> void infallibleAppend(const U* aBegin, const U* aEnd) {
|
||||
vec().infallibleAppend(aBegin, aEnd);
|
||||
}
|
||||
template<typename U> void infallibleAppend(const U* aBegin, size_t aLength) {
|
||||
vec().infallibleAppend(aBegin, aLength);
|
||||
}
|
||||
void popBack() { vec().popBack(); }
|
||||
T popCopy() { return vec().popCopy(); }
|
||||
template<typename U> T* insert(T* aP, U&& aVal) {
|
||||
return vec().insert(aP, mozilla::Forward<U>(aVal));
|
||||
}
|
||||
void erase(T* aT) { vec().erase(aT); }
|
||||
void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
|
||||
};
|
||||
|
||||
template <typename T, size_t N, typename AP>
|
||||
class RootedBase<JS::GCVector<T,N,AP>>
|
||||
: public MutableGCVectorOperations<JS::Rooted<JS::GCVector<T,N,AP>>, T,N,AP>
|
||||
{};
|
||||
|
||||
template <typename T, size_t N, typename AP>
|
||||
class MutableHandleBase<JS::GCVector<T,N,AP>>
|
||||
: public MutableGCVectorOperations<JS::MutableHandle<JS::GCVector<T,N,AP>>, T,N,AP>
|
||||
{};
|
||||
|
||||
template <typename T, size_t N, typename AP>
|
||||
class HandleBase<JS::GCVector<T,N,AP>>
|
||||
: public GCVectorOperations<JS::Handle<JS::GCVector<T,N,AP>>, T,N,AP>
|
||||
{};
|
||||
|
||||
template <typename T, size_t N, typename AP>
|
||||
class PersistentRootedBase<JS::GCVector<T,N,AP>>
|
||||
: public MutableGCVectorOperations<JS::PersistentRooted<JS::GCVector<T,N,AP>>, T,N,AP>
|
||||
{};
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // js_GCVector_h
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,406 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_HeapAPI_h
|
||||
#define js_HeapAPI_h
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/TraceKind.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
/* These values are private to the JS engine. */
|
||||
namespace js {
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
CurrentThreadCanAccessZone(JS::Zone* zone);
|
||||
|
||||
namespace gc {
|
||||
|
||||
struct Cell;
|
||||
|
||||
const size_t ArenaShift = 12;
|
||||
const size_t ArenaSize = size_t(1) << ArenaShift;
|
||||
const size_t ArenaMask = ArenaSize - 1;
|
||||
|
||||
#ifdef JS_GC_SMALL_CHUNK_SIZE
|
||||
const size_t ChunkShift = 18;
|
||||
#else
|
||||
const size_t ChunkShift = 20;
|
||||
#endif
|
||||
const size_t ChunkSize = size_t(1) << ChunkShift;
|
||||
const size_t ChunkMask = ChunkSize - 1;
|
||||
|
||||
const size_t CellShift = 3;
|
||||
const size_t CellSize = size_t(1) << CellShift;
|
||||
const size_t CellMask = CellSize - 1;
|
||||
|
||||
/* These are magic constants derived from actual offsets in gc/Heap.h. */
|
||||
#ifdef JS_GC_SMALL_CHUNK_SIZE
|
||||
const size_t ChunkMarkBitmapOffset = 258104;
|
||||
const size_t ChunkMarkBitmapBits = 31744;
|
||||
#else
|
||||
const size_t ChunkMarkBitmapOffset = 1032352;
|
||||
const size_t ChunkMarkBitmapBits = 129024;
|
||||
#endif
|
||||
const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
|
||||
const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
|
||||
const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
|
||||
const size_t ArenaZoneOffset = sizeof(size_t);
|
||||
const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
|
||||
sizeof(size_t) + sizeof(uintptr_t);
|
||||
|
||||
/*
|
||||
* Live objects are marked black. How many other additional colors are available
|
||||
* depends on the size of the GCThing. Objects marked gray are eligible for
|
||||
* cycle collection.
|
||||
*/
|
||||
static const uint32_t BLACK = 0;
|
||||
static const uint32_t GRAY = 1;
|
||||
|
||||
/*
|
||||
* The "location" field in the Chunk trailer is a enum indicating various roles
|
||||
* of the chunk.
|
||||
*/
|
||||
enum class ChunkLocation : uint32_t
|
||||
{
|
||||
Invalid = 0,
|
||||
Nursery = 1,
|
||||
TenuredHeap = 2
|
||||
};
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
/* When downcasting, ensure we are actually the right type. */
|
||||
extern JS_FRIEND_API(void)
|
||||
AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind);
|
||||
#else
|
||||
inline void
|
||||
AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {}
|
||||
#endif
|
||||
|
||||
MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
struct Zone;
|
||||
|
||||
/* Default size for the generational nursery in bytes. */
|
||||
const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
|
||||
|
||||
/* Default maximum heap size in bytes to pass to JS_NewRuntime(). */
|
||||
const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
|
||||
|
||||
namespace shadow {
|
||||
|
||||
struct Zone
|
||||
{
|
||||
protected:
|
||||
JSRuntime* const runtime_;
|
||||
JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|.
|
||||
|
||||
public:
|
||||
// Stack GC roots for Rooted GC pointers.
|
||||
js::RootedListHeads stackRoots_;
|
||||
template <typename T> friend class JS::Rooted;
|
||||
|
||||
bool needsIncrementalBarrier_;
|
||||
|
||||
Zone(JSRuntime* runtime, JSTracer* barrierTracerArg)
|
||||
: runtime_(runtime),
|
||||
barrierTracer_(barrierTracerArg),
|
||||
needsIncrementalBarrier_(false)
|
||||
{
|
||||
for (auto& stackRootPtr : stackRoots_)
|
||||
stackRootPtr = nullptr;
|
||||
}
|
||||
|
||||
bool needsIncrementalBarrier() const {
|
||||
return needsIncrementalBarrier_;
|
||||
}
|
||||
|
||||
JSTracer* barrierTracer() {
|
||||
MOZ_ASSERT(needsIncrementalBarrier_);
|
||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
|
||||
return barrierTracer_;
|
||||
}
|
||||
|
||||
JSRuntime* runtimeFromMainThread() const {
|
||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
|
||||
return runtime_;
|
||||
}
|
||||
|
||||
// Note: Unrestricted access to the zone's runtime from an arbitrary
|
||||
// thread can easily lead to races. Use this method very carefully.
|
||||
JSRuntime* runtimeFromAnyThread() const {
|
||||
return runtime_;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) {
|
||||
return reinterpret_cast<JS::shadow::Zone*>(zone);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace shadow */
|
||||
|
||||
/**
|
||||
* A GC pointer, tagged with the trace kind.
|
||||
*
|
||||
* In general, a GC pointer should be stored with an exact type. This class
|
||||
* is for use when that is not possible because a single pointer must point
|
||||
* to several kinds of GC thing.
|
||||
*/
|
||||
class JS_FRIEND_API(GCCellPtr)
|
||||
{
|
||||
public:
|
||||
// Construction from a void* and trace kind.
|
||||
GCCellPtr(void* gcthing, JS::TraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {}
|
||||
|
||||
// Automatically construct a null GCCellPtr from nullptr.
|
||||
MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
|
||||
|
||||
// Construction from an explicit type.
|
||||
template <typename T>
|
||||
explicit GCCellPtr(T* p) : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) { }
|
||||
explicit GCCellPtr(JSFunction* p) : ptr(checkedCast(p, JS::TraceKind::Object)) { }
|
||||
explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
|
||||
explicit GCCellPtr(const Value& v);
|
||||
|
||||
JS::TraceKind kind() const {
|
||||
JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
|
||||
if (uintptr_t(traceKind) != OutOfLineTraceKindMask)
|
||||
return traceKind;
|
||||
return outOfLineKind();
|
||||
}
|
||||
|
||||
// Allow GCCellPtr to be used in a boolean context.
|
||||
explicit operator bool() const {
|
||||
MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null));
|
||||
return asCell();
|
||||
}
|
||||
|
||||
// Simplify checks to the kind.
|
||||
template <typename T>
|
||||
bool is() const { return kind() == JS::MapTypeToTraceKind<T>::kind; }
|
||||
|
||||
// Conversions to more specific types must match the kind. Access to
|
||||
// further refined types is not allowed directly from a GCCellPtr.
|
||||
template <typename T>
|
||||
T& as() const {
|
||||
MOZ_ASSERT(kind() == JS::MapTypeToTraceKind<T>::kind);
|
||||
// We can't use static_cast here, because the fact that JSObject
|
||||
// inherits from js::gc::Cell is not part of the public API.
|
||||
return *reinterpret_cast<T*>(asCell());
|
||||
}
|
||||
|
||||
// Return a pointer to the cell this |GCCellPtr| refers to, or |nullptr|.
|
||||
// (It would be more symmetrical with |to| for this to return a |Cell&|, but
|
||||
// the result can be |nullptr|, and null references are undefined behavior.)
|
||||
js::gc::Cell* asCell() const {
|
||||
return reinterpret_cast<js::gc::Cell*>(ptr & ~OutOfLineTraceKindMask);
|
||||
}
|
||||
|
||||
// The CC's trace logger needs an identity that is XPIDL serializable.
|
||||
uint64_t unsafeAsInteger() const {
|
||||
return static_cast<uint64_t>(unsafeAsUIntPtr());
|
||||
}
|
||||
// Inline mark bitmap access requires direct pointer arithmetic.
|
||||
uintptr_t unsafeAsUIntPtr() const {
|
||||
MOZ_ASSERT(asCell());
|
||||
MOZ_ASSERT(!js::gc::IsInsideNursery(asCell()));
|
||||
return reinterpret_cast<uintptr_t>(asCell());
|
||||
}
|
||||
|
||||
bool mayBeOwnedByOtherRuntime() const;
|
||||
|
||||
private:
|
||||
static uintptr_t checkedCast(void* p, JS::TraceKind traceKind) {
|
||||
js::gc::Cell* cell = static_cast<js::gc::Cell*>(p);
|
||||
MOZ_ASSERT((uintptr_t(p) & OutOfLineTraceKindMask) == 0);
|
||||
AssertGCThingHasType(cell, traceKind);
|
||||
// Note: the OutOfLineTraceKindMask bits are set on all out-of-line kinds
|
||||
// so that we can mask instead of branching.
|
||||
MOZ_ASSERT_IF(uintptr_t(traceKind) >= OutOfLineTraceKindMask,
|
||||
(uintptr_t(traceKind) & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
|
||||
return uintptr_t(p) | (uintptr_t(traceKind) & OutOfLineTraceKindMask);
|
||||
}
|
||||
|
||||
JS::TraceKind outOfLineKind() const;
|
||||
|
||||
uintptr_t ptr;
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
|
||||
{
|
||||
return ptr1.asCell() == ptr2.asCell();
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
|
||||
{
|
||||
return !(ptr1 == ptr2);
|
||||
}
|
||||
|
||||
// Unwraps the given GCCellPtr and calls the given functor with a template
|
||||
// argument of the actual type of the pointer.
|
||||
template <typename F, typename... Args>
|
||||
auto
|
||||
DispatchTyped(F f, GCCellPtr thing, Args&&... args)
|
||||
-> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
|
||||
{
|
||||
switch (thing.kind()) {
|
||||
#define JS_EXPAND_DEF(name, type, _) \
|
||||
case JS::TraceKind::name: \
|
||||
return f(&thing.as<type>(), mozilla::Forward<Args>(args)...);
|
||||
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
|
||||
#undef JS_EXPAND_DEF
|
||||
default:
|
||||
MOZ_CRASH("Invalid trace kind in DispatchTyped for GCCellPtr.");
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
namespace detail {
|
||||
|
||||
static MOZ_ALWAYS_INLINE uintptr_t*
|
||||
GetGCThingMarkBitmap(const uintptr_t addr)
|
||||
{
|
||||
MOZ_ASSERT(addr);
|
||||
const uintptr_t bmap_addr = (addr & ~ChunkMask) | ChunkMarkBitmapOffset;
|
||||
return reinterpret_cast<uintptr_t*>(bmap_addr);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
GetGCThingMarkWordAndMask(const uintptr_t addr, uint32_t color,
|
||||
uintptr_t** wordp, uintptr_t* maskp)
|
||||
{
|
||||
MOZ_ASSERT(addr);
|
||||
const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellSize + color;
|
||||
MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits);
|
||||
uintptr_t* bitmap = GetGCThingMarkBitmap(addr);
|
||||
const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT;
|
||||
*maskp = uintptr_t(1) << (bit % nbits);
|
||||
*wordp = &bitmap[bit / nbits];
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE JS::Zone*
|
||||
GetGCThingZone(const uintptr_t addr)
|
||||
{
|
||||
MOZ_ASSERT(addr);
|
||||
const uintptr_t zone_addr = (addr & ~ArenaMask) | ArenaZoneOffset;
|
||||
return *reinterpret_cast<JS::Zone**>(zone_addr);
|
||||
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE JS::shadow::Runtime*
|
||||
GetCellRuntime(const Cell* cell)
|
||||
{
|
||||
MOZ_ASSERT(cell);
|
||||
const uintptr_t addr = uintptr_t(cell);
|
||||
const uintptr_t rt_addr = (addr & ~ChunkMask) | ChunkRuntimeOffset;
|
||||
return *reinterpret_cast<JS::shadow::Runtime**>(rt_addr);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
CellIsMarkedGray(const Cell* cell)
|
||||
{
|
||||
MOZ_ASSERT(cell);
|
||||
if (js::gc::IsInsideNursery(cell))
|
||||
return false;
|
||||
|
||||
uintptr_t* word, mask;
|
||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::GRAY, &word, &mask);
|
||||
return *word & mask;
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
CellIsMarkedGrayIfKnown(const Cell* cell);
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
IsInsideNursery(const js::gc::Cell* cell)
|
||||
{
|
||||
if (!cell)
|
||||
return false;
|
||||
uintptr_t addr = uintptr_t(cell);
|
||||
addr &= ~js::gc::ChunkMask;
|
||||
addr |= js::gc::ChunkLocationOffset;
|
||||
auto location = *reinterpret_cast<ChunkLocation*>(addr);
|
||||
MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
|
||||
return location == ChunkLocation::Nursery;
|
||||
}
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
|
||||
static MOZ_ALWAYS_INLINE Zone*
|
||||
GetTenuredGCThingZone(GCCellPtr thing)
|
||||
{
|
||||
MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
|
||||
return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr());
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE Zone*
|
||||
GetStringZone(JSString* str)
|
||||
{
|
||||
return js::gc::detail::GetGCThingZone(uintptr_t(str));
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(Zone*)
|
||||
GetObjectZone(JSObject* obj);
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
GCThingIsMarkedGray(GCCellPtr thing)
|
||||
{
|
||||
if (thing.mayBeOwnedByOtherRuntime())
|
||||
return false;
|
||||
return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell());
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(JS::TraceKind)
|
||||
GCThingTraceKind(void* thing);
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsIncrementalBarrierNeededOnTenuredGCThing(JS::shadow::Runtime* rt, const JS::GCCellPtr thing)
|
||||
{
|
||||
MOZ_ASSERT(thing);
|
||||
MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
|
||||
|
||||
// TODO: I'd like to assert !isHeapBusy() here but this gets called while we
|
||||
// are tracing the heap, e.g. during memory reporting (see bug 1313318).
|
||||
MOZ_ASSERT(!rt->isHeapCollecting());
|
||||
|
||||
JS::Zone* zone = JS::GetTenuredGCThingZone(thing);
|
||||
return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an object providing access to the garbage collector's internal notion
|
||||
* of the current state of memory (both GC heap memory and GCthing-controlled
|
||||
* malloc memory.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
NewMemoryInfoObject(JSContext* cx);
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* js_HeapAPI_h */
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_Id_h
|
||||
#define js_Id_h
|
||||
|
||||
// A jsid is an identifier for a property or method of an object which is
|
||||
// either a 31-bit unsigned integer, interned string or symbol.
|
||||
//
|
||||
// Also, there is an additional jsid value, JSID_VOID, which does not occur in
|
||||
// JS scripts but may be used to indicate the absence of a valid jsid. A void
|
||||
// jsid is not a valid id and only arises as an exceptional API return value,
|
||||
// such as in JS_NextProperty. Embeddings must not pass JSID_VOID into JSAPI
|
||||
// entry points expecting a jsid and do not need to handle JSID_VOID in hooks
|
||||
// receiving a jsid except when explicitly noted in the API contract.
|
||||
//
|
||||
// A jsid is not implicitly convertible to or from a Value; JS_ValueToId or
|
||||
// JS_IdToValue must be used instead.
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
struct jsid
|
||||
{
|
||||
size_t asBits;
|
||||
bool operator==(const jsid& rhs) const { return asBits == rhs.asBits; }
|
||||
bool operator!=(const jsid& rhs) const { return asBits != rhs.asBits; }
|
||||
} JS_HAZ_GC_POINTER;
|
||||
#define JSID_BITS(id) (id.asBits)
|
||||
|
||||
#define JSID_TYPE_STRING 0x0
|
||||
#define JSID_TYPE_INT 0x1
|
||||
#define JSID_TYPE_VOID 0x2
|
||||
#define JSID_TYPE_SYMBOL 0x4
|
||||
#define JSID_TYPE_MASK 0x7
|
||||
|
||||
// Avoid using canonical 'id' for jsid parameters since this is a magic word in
|
||||
// Objective-C++ which, apparently, wants to be able to #include jsapi.h.
|
||||
#define id iden
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
JSID_IS_STRING(jsid id)
|
||||
{
|
||||
return (JSID_BITS(id) & JSID_TYPE_MASK) == 0;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE JSString*
|
||||
JSID_TO_STRING(jsid id)
|
||||
{
|
||||
MOZ_ASSERT(JSID_IS_STRING(id));
|
||||
return (JSString*)JSID_BITS(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only JSStrings that have been interned via the JSAPI can be turned into
|
||||
* jsids by API clients.
|
||||
*
|
||||
* N.B. if a jsid is backed by a string which has not been interned, that
|
||||
* string must be appropriately rooted to avoid being collected by the GC.
|
||||
*/
|
||||
JS_PUBLIC_API(jsid)
|
||||
INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str);
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
JSID_IS_INT(jsid id)
|
||||
{
|
||||
return !!(JSID_BITS(id) & JSID_TYPE_INT);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE int32_t
|
||||
JSID_TO_INT(jsid id)
|
||||
{
|
||||
MOZ_ASSERT(JSID_IS_INT(id));
|
||||
return ((uint32_t)JSID_BITS(id)) >> 1;
|
||||
}
|
||||
|
||||
#define JSID_INT_MIN 0
|
||||
#define JSID_INT_MAX INT32_MAX
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
INT_FITS_IN_JSID(int32_t i)
|
||||
{
|
||||
return i >= 0;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE jsid
|
||||
INT_TO_JSID(int32_t i)
|
||||
{
|
||||
jsid id;
|
||||
MOZ_ASSERT(INT_FITS_IN_JSID(i));
|
||||
JSID_BITS(id) = ((i << 1) | JSID_TYPE_INT);
|
||||
return id;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
JSID_IS_SYMBOL(jsid id)
|
||||
{
|
||||
return (JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_SYMBOL &&
|
||||
JSID_BITS(id) != JSID_TYPE_SYMBOL;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE JS::Symbol*
|
||||
JSID_TO_SYMBOL(jsid id)
|
||||
{
|
||||
MOZ_ASSERT(JSID_IS_SYMBOL(id));
|
||||
return (JS::Symbol*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE jsid
|
||||
SYMBOL_TO_JSID(JS::Symbol* sym)
|
||||
{
|
||||
jsid id;
|
||||
MOZ_ASSERT(sym != nullptr);
|
||||
MOZ_ASSERT((size_t(sym) & JSID_TYPE_MASK) == 0);
|
||||
MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym)));
|
||||
JSID_BITS(id) = (size_t(sym) | JSID_TYPE_SYMBOL);
|
||||
return id;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
JSID_IS_GCTHING(jsid id)
|
||||
{
|
||||
return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE JS::GCCellPtr
|
||||
JSID_TO_GCTHING(jsid id)
|
||||
{
|
||||
void* thing = (void*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
|
||||
if (JSID_IS_STRING(id))
|
||||
return JS::GCCellPtr(thing, JS::TraceKind::String);
|
||||
MOZ_ASSERT(JSID_IS_SYMBOL(id));
|
||||
return JS::GCCellPtr(thing, JS::TraceKind::Symbol);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
JSID_IS_VOID(const jsid id)
|
||||
{
|
||||
MOZ_ASSERT_IF(((size_t)JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_VOID,
|
||||
JSID_BITS(id) == JSID_TYPE_VOID);
|
||||
return (size_t)JSID_BITS(id) == JSID_TYPE_VOID;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
JSID_IS_EMPTY(const jsid id)
|
||||
{
|
||||
return (size_t)JSID_BITS(id) == JSID_TYPE_SYMBOL;
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_DATA(const jsid) JSID_VOID;
|
||||
extern JS_PUBLIC_DATA(const jsid) JSID_EMPTY;
|
||||
|
||||
extern JS_PUBLIC_DATA(const JS::HandleId) JSID_VOIDHANDLE;
|
||||
extern JS_PUBLIC_DATA(const JS::HandleId) JSID_EMPTYHANDLE;
|
||||
|
||||
namespace JS {
|
||||
|
||||
template <>
|
||||
struct GCPolicy<jsid>
|
||||
{
|
||||
static jsid initial() { return JSID_VOID; }
|
||||
static void trace(JSTracer* trc, jsid* idp, const char* name) {
|
||||
js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
template <>
|
||||
struct BarrierMethods<jsid>
|
||||
{
|
||||
static void postBarrier(jsid* idp, jsid prev, jsid next) {}
|
||||
static void exposeToJS(jsid id) {
|
||||
if (JSID_IS_GCTHING(id))
|
||||
js::gc::ExposeGCThingToActiveJS(JSID_TO_GCTHING(id));
|
||||
}
|
||||
};
|
||||
|
||||
// If the jsid is a GC pointer type, convert to that type and call |f| with
|
||||
// the pointer. If the jsid is not a GC type, calls F::defaultValue.
|
||||
template <typename F, typename... Args>
|
||||
auto
|
||||
DispatchTyped(F f, const jsid& id, Args&&... args)
|
||||
-> decltype(f(static_cast<JSString*>(nullptr), mozilla::Forward<Args>(args)...))
|
||||
{
|
||||
if (JSID_IS_STRING(id))
|
||||
return f(JSID_TO_STRING(id), mozilla::Forward<Args>(args)...);
|
||||
if (JSID_IS_SYMBOL(id))
|
||||
return f(JSID_TO_SYMBOL(id), mozilla::Forward<Args>(args)...);
|
||||
MOZ_ASSERT(!JSID_IS_GCTHING(id));
|
||||
return F::defaultValue(id);
|
||||
}
|
||||
|
||||
#undef id
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* js_Id_h */
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; 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/. */
|
||||
|
||||
/* SpiderMonkey initialization and shutdown APIs. */
|
||||
|
||||
#ifndef js_Initialization_h
|
||||
#define js_Initialization_h
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
namespace JS {
|
||||
namespace detail {
|
||||
|
||||
enum class InitState { Uninitialized = 0, Running, ShutDown };
|
||||
|
||||
/**
|
||||
* SpiderMonkey's initialization status is tracked here, and it controls things
|
||||
* that should happen only once across all runtimes. It's an API requirement
|
||||
* that JS_Init (and JS_ShutDown, if called) be called in a thread-aware
|
||||
* manner, so this (internal -- embedders, don't use!) variable doesn't need to
|
||||
* be atomic.
|
||||
*/
|
||||
extern JS_PUBLIC_DATA(InitState)
|
||||
libraryInitState;
|
||||
|
||||
extern JS_PUBLIC_API(const char*)
|
||||
InitWithFailureDiagnostic(bool isDebugBuild);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace JS
|
||||
|
||||
// These are equivalent to ICU's |UMemAllocFn|, |UMemReallocFn|, and
|
||||
// |UMemFreeFn| types. The first argument (called |context| in the ICU docs)
|
||||
// will always be nullptr and should be ignored.
|
||||
typedef void* (*JS_ICUAllocFn)(const void*, size_t size);
|
||||
typedef void* (*JS_ICUReallocFn)(const void*, void* p, size_t size);
|
||||
typedef void (*JS_ICUFreeFn)(const void*, void* p);
|
||||
|
||||
/**
|
||||
* This function can be used to track memory used by ICU. If it is called, it
|
||||
* *must* be called before JS_Init. Don't use it unless you know what you're
|
||||
* doing!
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
|
||||
JS_ICUReallocFn reallocFn,
|
||||
JS_ICUFreeFn freeFn);
|
||||
|
||||
/**
|
||||
* Initialize SpiderMonkey, returning true only if initialization succeeded.
|
||||
* Once this method has succeeded, it is safe to call JS_NewRuntime and other
|
||||
* JSAPI methods.
|
||||
*
|
||||
* This method must be called before any other JSAPI method is used on any
|
||||
* thread. Once it has been used, it is safe to call any JSAPI method, and it
|
||||
* remains safe to do so until JS_ShutDown is correctly called.
|
||||
*
|
||||
* It is currently not possible to initialize SpiderMonkey multiple times (that
|
||||
* is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
|
||||
* again). This restriction may eventually be lifted.
|
||||
*/
|
||||
inline bool
|
||||
JS_Init(void)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
return !JS::detail::InitWithFailureDiagnostic(true);
|
||||
#else
|
||||
return !JS::detail::InitWithFailureDiagnostic(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* A variant of JS_Init. On success it returns nullptr. On failure it returns a
|
||||
* pointer to a string literal that describes how initialization failed, which
|
||||
* can be useful for debugging purposes.
|
||||
*/
|
||||
inline const char*
|
||||
JS_InitWithFailureDiagnostic(void)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
return JS::detail::InitWithFailureDiagnostic(true);
|
||||
#else
|
||||
return JS::detail::InitWithFailureDiagnostic(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if SpiderMonkey has been initialized successfully, even if it has
|
||||
* possibly been shut down.
|
||||
*
|
||||
* Note that it is the responsibility of the embedder to call JS_Init() and
|
||||
* JS_ShutDown() at the correct times, and therefore this API should ideally not
|
||||
* be necessary to use. This is only intended to be used in cases where the
|
||||
* embedder isn't in full control of deciding whether to initialize SpiderMonkey
|
||||
* or hand off the task to another consumer.
|
||||
*/
|
||||
inline bool
|
||||
JS_IsInitialized(void)
|
||||
{
|
||||
return JS::detail::libraryInitState != JS::detail::InitState::Uninitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy free-standing resources allocated by SpiderMonkey, not associated
|
||||
* with any runtime, context, or other structure.
|
||||
*
|
||||
* This method should be called after all other JSAPI data has been properly
|
||||
* cleaned up: every new runtime must have been destroyed, every new context
|
||||
* must have been destroyed, and so on. Calling this method before all other
|
||||
* resources have been destroyed has undefined behavior.
|
||||
*
|
||||
* Failure to call this method, at present, has no adverse effects other than
|
||||
* leaking memory. This may not always be the case; it's recommended that all
|
||||
* embedders call this method when all other JSAPI operations have completed.
|
||||
*
|
||||
* It is currently not possible to initialize SpiderMonkey multiple times (that
|
||||
* is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
|
||||
* again). This restriction may eventually be lifted.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_ShutDown(void);
|
||||
|
||||
#endif /* js_Initialization_h */
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/*
|
||||
* This section typedefs the old 'native' types to the new <stdint.h> types.
|
||||
* These redefinitions are provided solely to allow JSAPI users to more easily
|
||||
* transition to <stdint.h> types. They are not to be used in the JSAPI, and
|
||||
* new JSAPI user code should not use them. This mapping file may eventually
|
||||
* be removed from SpiderMonkey, so don't depend on it in the long run.
|
||||
*/
|
||||
|
||||
/*
|
||||
* BEWARE: Comity with other implementers of these types is not guaranteed.
|
||||
* Indeed, if you use this header and third-party code defining these
|
||||
* types, *expect* to encounter either compile errors or link errors,
|
||||
* depending how these types are used and on the order of inclusion.
|
||||
* It is safest to use only the <stdint.h> types.
|
||||
*/
|
||||
#ifndef js_LegacyIntTypes_h
|
||||
#define js_LegacyIntTypes_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "js-config.h"
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
/*
|
||||
* On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very
|
||||
* common header file) defines the types int8, int16, int32, and int64.
|
||||
* So we don't define these four types here to avoid conflicts in case
|
||||
* the code also includes sys/types.h.
|
||||
*/
|
||||
#if defined(AIX) && defined(HAVE_SYS_INTTYPES_H)
|
||||
#include <sys/inttypes.h>
|
||||
#else
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
#endif /* AIX && HAVE_SYS_INTTYPES_H */
|
||||
|
||||
typedef uint8_t JSUint8;
|
||||
typedef uint16_t JSUint16;
|
||||
typedef uint32_t JSUint32;
|
||||
typedef uint64_t JSUint64;
|
||||
|
||||
typedef int8_t JSInt8;
|
||||
typedef int16_t JSInt16;
|
||||
typedef int32_t JSInt32;
|
||||
typedef int64_t JSInt64;
|
||||
|
||||
#endif /* js_LegacyIntTypes_h */
|
||||
|
|
@ -1,971 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_MemoryMetrics_h
|
||||
#define js_MemoryMetrics_h
|
||||
|
||||
// These declarations are highly likely to change in the future. Depend on them
|
||||
// at your own risk.
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "jsalloc.h"
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/HashTable.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
class nsISupports; // Needed for ObjectPrivateVisitor.
|
||||
|
||||
namespace JS {
|
||||
|
||||
struct TabSizes
|
||||
{
|
||||
enum Kind {
|
||||
Objects,
|
||||
Strings,
|
||||
Private,
|
||||
Other
|
||||
};
|
||||
|
||||
TabSizes() { mozilla::PodZero(this); }
|
||||
|
||||
void add(Kind kind, size_t n) {
|
||||
switch (kind) {
|
||||
case Objects: objects += n; break;
|
||||
case Strings: strings += n; break;
|
||||
case Private: private_ += n; break;
|
||||
case Other: other += n; break;
|
||||
default: MOZ_CRASH("bad TabSizes kind");
|
||||
}
|
||||
}
|
||||
|
||||
size_t objects;
|
||||
size_t strings;
|
||||
size_t private_;
|
||||
size_t other;
|
||||
};
|
||||
|
||||
/** These are the measurements used by Servo. */
|
||||
struct ServoSizes
|
||||
{
|
||||
enum Kind {
|
||||
GCHeapUsed,
|
||||
GCHeapUnused,
|
||||
GCHeapAdmin,
|
||||
GCHeapDecommitted,
|
||||
MallocHeap,
|
||||
NonHeap,
|
||||
Ignore
|
||||
};
|
||||
|
||||
ServoSizes() { mozilla::PodZero(this); }
|
||||
|
||||
void add(Kind kind, size_t n) {
|
||||
switch (kind) {
|
||||
case GCHeapUsed: gcHeapUsed += n; break;
|
||||
case GCHeapUnused: gcHeapUnused += n; break;
|
||||
case GCHeapAdmin: gcHeapAdmin += n; break;
|
||||
case GCHeapDecommitted: gcHeapDecommitted += n; break;
|
||||
case MallocHeap: mallocHeap += n; break;
|
||||
case NonHeap: nonHeap += n; break;
|
||||
case Ignore: /* do nothing */ break;
|
||||
default: MOZ_CRASH("bad ServoSizes kind");
|
||||
}
|
||||
}
|
||||
|
||||
size_t gcHeapUsed;
|
||||
size_t gcHeapUnused;
|
||||
size_t gcHeapAdmin;
|
||||
size_t gcHeapDecommitted;
|
||||
size_t mallocHeap;
|
||||
size_t nonHeap;
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
/**
|
||||
* In memory reporting, we have concept of "sundries", line items which are too
|
||||
* small to be worth reporting individually. Under some circumstances, a memory
|
||||
* reporter gets tossed into the sundries bucket if it's smaller than
|
||||
* MemoryReportingSundriesThreshold() bytes.
|
||||
*
|
||||
* We need to define this value here, rather than in the code which actually
|
||||
* generates the memory reports, because NotableStringInfo uses this value.
|
||||
*/
|
||||
JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold();
|
||||
|
||||
/**
|
||||
* This hash policy avoids flattening ropes (which perturbs the site being
|
||||
* measured and requires a JSContext) at the expense of doing a FULL ROPE COPY
|
||||
* on every hash and match! Beware.
|
||||
*/
|
||||
struct InefficientNonFlatteningStringHashPolicy
|
||||
{
|
||||
typedef JSString* Lookup;
|
||||
static HashNumber hash(const Lookup& l);
|
||||
static bool match(const JSString* const& k, const Lookup& l);
|
||||
};
|
||||
|
||||
struct CStringHashPolicy
|
||||
{
|
||||
typedef const char* Lookup;
|
||||
static HashNumber hash(const Lookup& l);
|
||||
static bool match(const char* const& k, const Lookup& l);
|
||||
};
|
||||
|
||||
// This file features many classes with numerous size_t fields, and each such
|
||||
// class has one or more methods that need to operate on all of these fields.
|
||||
// Writing these individually is error-prone -- it's easy to add a new field
|
||||
// without updating all the required methods. So we define a single macro list
|
||||
// in each class to name the fields (and notable characteristics of them), and
|
||||
// then use the following macros to transform those lists into the required
|
||||
// methods.
|
||||
//
|
||||
// - The |tabKind| value is used when measuring TabSizes.
|
||||
//
|
||||
// - The |servoKind| value is used when measuring ServoSizes and also for
|
||||
// the various sizeOfLiveGCThings() methods.
|
||||
//
|
||||
// In some classes, one or more of the macro arguments aren't used. We use '_'
|
||||
// for those.
|
||||
//
|
||||
#define DECL_SIZE(tabKind, servoKind, mSize) size_t mSize;
|
||||
#define ZERO_SIZE(tabKind, servoKind, mSize) mSize(0),
|
||||
#define COPY_OTHER_SIZE(tabKind, servoKind, mSize) mSize(other.mSize),
|
||||
#define ADD_OTHER_SIZE(tabKind, servoKind, mSize) mSize += other.mSize;
|
||||
#define SUB_OTHER_SIZE(tabKind, servoKind, mSize) \
|
||||
MOZ_ASSERT(mSize >= other.mSize); \
|
||||
mSize -= other.mSize;
|
||||
#define ADD_SIZE_TO_N(tabKind, servoKind, mSize) n += mSize;
|
||||
#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize) \
|
||||
/* Avoid self-comparison warnings by comparing enums indirectly. */ \
|
||||
n += (mozilla::IsSame<int[ServoSizes::servoKind], int[ServoSizes::GCHeapUsed]>::value) \
|
||||
? mSize \
|
||||
: 0;
|
||||
#define ADD_TO_TAB_SIZES(tabKind, servoKind, mSize) sizes->add(JS::TabSizes::tabKind, mSize);
|
||||
#define ADD_TO_SERVO_SIZES(tabKind, servoKind, mSize) sizes->add(JS::ServoSizes::servoKind, mSize);
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
struct ClassInfo
|
||||
{
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(Objects, GCHeapUsed, objectsGCHeap) \
|
||||
macro(Objects, MallocHeap, objectsMallocHeapSlots) \
|
||||
macro(Objects, MallocHeap, objectsMallocHeapElementsNormal) \
|
||||
macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \
|
||||
macro(Objects, MallocHeap, objectsMallocHeapMisc) \
|
||||
macro(Objects, NonHeap, objectsNonHeapElementsNormal) \
|
||||
macro(Objects, NonHeap, objectsNonHeapElementsShared) \
|
||||
macro(Objects, NonHeap, objectsNonHeapElementsWasm) \
|
||||
macro(Objects, NonHeap, objectsNonHeapCodeWasm)
|
||||
|
||||
ClassInfo()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
wasmGuardPages(0)
|
||||
{}
|
||||
|
||||
void add(const ClassInfo& other) {
|
||||
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
||||
}
|
||||
|
||||
void subtract(const ClassInfo& other) {
|
||||
FOR_EACH_SIZE(SUB_OTHER_SIZE)
|
||||
}
|
||||
|
||||
size_t sizeOfAllThings() const {
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N)
|
||||
return n;
|
||||
}
|
||||
|
||||
bool isNotable() const {
|
||||
static const size_t NotabilityThreshold = 16 * 1024;
|
||||
return sizeOfAllThings() >= NotabilityThreshold;
|
||||
}
|
||||
|
||||
size_t sizeOfLiveGCThings() const {
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
|
||||
return n;
|
||||
}
|
||||
|
||||
void addToTabSizes(TabSizes* sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
|
||||
}
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
}
|
||||
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
size_t wasmGuardPages;
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
struct ShapeInfo
|
||||
{
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(Other, GCHeapUsed, shapesGCHeapTree) \
|
||||
macro(Other, GCHeapUsed, shapesGCHeapDict) \
|
||||
macro(Other, GCHeapUsed, shapesGCHeapBase) \
|
||||
macro(Other, MallocHeap, shapesMallocHeapTreeTables) \
|
||||
macro(Other, MallocHeap, shapesMallocHeapDictTables) \
|
||||
macro(Other, MallocHeap, shapesMallocHeapTreeKids)
|
||||
|
||||
ShapeInfo()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
dummy()
|
||||
{}
|
||||
|
||||
void add(const ShapeInfo& other) {
|
||||
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
||||
}
|
||||
|
||||
void subtract(const ShapeInfo& other) {
|
||||
FOR_EACH_SIZE(SUB_OTHER_SIZE)
|
||||
}
|
||||
|
||||
size_t sizeOfAllThings() const {
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N)
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t sizeOfLiveGCThings() const {
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
|
||||
return n;
|
||||
}
|
||||
|
||||
void addToTabSizes(TabSizes* sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
|
||||
}
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
}
|
||||
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds data about a notable class (one whose combined object and shape
|
||||
* instances use more than a certain amount of memory) so we can report it
|
||||
* individually.
|
||||
*
|
||||
* The only difference between this class and ClassInfo is that this class
|
||||
* holds a copy of the filename.
|
||||
*/
|
||||
struct NotableClassInfo : public ClassInfo
|
||||
{
|
||||
NotableClassInfo();
|
||||
NotableClassInfo(const char* className, const ClassInfo& info);
|
||||
NotableClassInfo(NotableClassInfo&& info);
|
||||
NotableClassInfo& operator=(NotableClassInfo&& info);
|
||||
|
||||
~NotableClassInfo() {
|
||||
js_free(className_);
|
||||
}
|
||||
|
||||
char* className_;
|
||||
|
||||
private:
|
||||
NotableClassInfo(const NotableClassInfo& info) = delete;
|
||||
};
|
||||
|
||||
/** Data for tracking JIT-code memory usage. */
|
||||
struct CodeSizes
|
||||
{
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(_, NonHeap, ion) \
|
||||
macro(_, NonHeap, baseline) \
|
||||
macro(_, NonHeap, regexp) \
|
||||
macro(_, NonHeap, other) \
|
||||
macro(_, NonHeap, unused)
|
||||
|
||||
CodeSizes()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
dummy()
|
||||
{}
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
}
|
||||
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
/** Data for tracking GC memory usage. */
|
||||
struct GCSizes
|
||||
{
|
||||
// |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted
|
||||
// because we don't consider the nursery to be part of the GC heap.
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(_, MallocHeap, marker) \
|
||||
macro(_, NonHeap, nurseryCommitted) \
|
||||
macro(_, MallocHeap, nurseryMallocedBuffers) \
|
||||
macro(_, MallocHeap, storeBufferVals) \
|
||||
macro(_, MallocHeap, storeBufferCells) \
|
||||
macro(_, MallocHeap, storeBufferSlots) \
|
||||
macro(_, MallocHeap, storeBufferWholeCells) \
|
||||
macro(_, MallocHeap, storeBufferGenerics)
|
||||
|
||||
GCSizes()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
dummy()
|
||||
{}
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
}
|
||||
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds information about the memory taken up by identical copies of
|
||||
* a particular string. Multiple JSStrings may have their sizes aggregated
|
||||
* together into one StringInfo object. Note that two strings with identical
|
||||
* chars will not be aggregated together if one is a short string and the other
|
||||
* is not.
|
||||
*/
|
||||
struct StringInfo
|
||||
{
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(Strings, GCHeapUsed, gcHeapLatin1) \
|
||||
macro(Strings, GCHeapUsed, gcHeapTwoByte) \
|
||||
macro(Strings, MallocHeap, mallocHeapLatin1) \
|
||||
macro(Strings, MallocHeap, mallocHeapTwoByte)
|
||||
|
||||
StringInfo()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
numCopies(0)
|
||||
{}
|
||||
|
||||
void add(const StringInfo& other) {
|
||||
FOR_EACH_SIZE(ADD_OTHER_SIZE);
|
||||
numCopies++;
|
||||
}
|
||||
|
||||
void subtract(const StringInfo& other) {
|
||||
FOR_EACH_SIZE(SUB_OTHER_SIZE);
|
||||
numCopies--;
|
||||
}
|
||||
|
||||
bool isNotable() const {
|
||||
static const size_t NotabilityThreshold = 16 * 1024;
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N)
|
||||
return n >= NotabilityThreshold;
|
||||
}
|
||||
|
||||
size_t sizeOfLiveGCThings() const {
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
|
||||
return n;
|
||||
}
|
||||
|
||||
void addToTabSizes(TabSizes* sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
|
||||
}
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
}
|
||||
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
uint32_t numCopies; // How many copies of the string have we seen?
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds data about a notable string (one which, counting all duplicates, uses
|
||||
* more than a certain amount of memory) so we can report it individually.
|
||||
*
|
||||
* The only difference between this class and StringInfo is that
|
||||
* NotableStringInfo holds a copy of some or all of the string's chars.
|
||||
*/
|
||||
struct NotableStringInfo : public StringInfo
|
||||
{
|
||||
static const size_t MAX_SAVED_CHARS = 1024;
|
||||
|
||||
NotableStringInfo();
|
||||
NotableStringInfo(JSString* str, const StringInfo& info);
|
||||
NotableStringInfo(NotableStringInfo&& info);
|
||||
NotableStringInfo& operator=(NotableStringInfo&& info);
|
||||
|
||||
~NotableStringInfo() {
|
||||
js_free(buffer);
|
||||
}
|
||||
|
||||
char* buffer;
|
||||
size_t length;
|
||||
|
||||
private:
|
||||
NotableStringInfo(const NotableStringInfo& info) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds information about the memory taken up by script sources
|
||||
* from a particular file.
|
||||
*/
|
||||
struct ScriptSourceInfo
|
||||
{
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(_, MallocHeap, misc)
|
||||
|
||||
ScriptSourceInfo()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
numScripts(0)
|
||||
{}
|
||||
|
||||
void add(const ScriptSourceInfo& other) {
|
||||
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
||||
numScripts++;
|
||||
}
|
||||
|
||||
void subtract(const ScriptSourceInfo& other) {
|
||||
FOR_EACH_SIZE(SUB_OTHER_SIZE)
|
||||
numScripts--;
|
||||
}
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
}
|
||||
|
||||
bool isNotable() const {
|
||||
static const size_t NotabilityThreshold = 16 * 1024;
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N)
|
||||
return n >= NotabilityThreshold;
|
||||
}
|
||||
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
uint32_t numScripts; // How many ScriptSources come from this file? (It
|
||||
// can be more than one in XML files that have
|
||||
// multiple scripts in CDATA sections.)
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds data about a notable script source file (one whose combined
|
||||
* script sources use more than a certain amount of memory) so we can report it
|
||||
* individually.
|
||||
*
|
||||
* The only difference between this class and ScriptSourceInfo is that this
|
||||
* class holds a copy of the filename.
|
||||
*/
|
||||
struct NotableScriptSourceInfo : public ScriptSourceInfo
|
||||
{
|
||||
NotableScriptSourceInfo();
|
||||
NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info);
|
||||
NotableScriptSourceInfo(NotableScriptSourceInfo&& info);
|
||||
NotableScriptSourceInfo& operator=(NotableScriptSourceInfo&& info);
|
||||
|
||||
~NotableScriptSourceInfo() {
|
||||
js_free(filename_);
|
||||
}
|
||||
|
||||
char* filename_;
|
||||
|
||||
private:
|
||||
NotableScriptSourceInfo(const NotableScriptSourceInfo& info) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* These measurements relate directly to the JSRuntime, and not to zones and
|
||||
* compartments within it.
|
||||
*/
|
||||
struct RuntimeSizes
|
||||
{
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(_, MallocHeap, object) \
|
||||
macro(_, MallocHeap, atomsTable) \
|
||||
macro(_, MallocHeap, contexts) \
|
||||
macro(_, MallocHeap, temporary) \
|
||||
macro(_, MallocHeap, interpreterStack) \
|
||||
macro(_, MallocHeap, mathCache) \
|
||||
macro(_, MallocHeap, sharedImmutableStringsCache) \
|
||||
macro(_, MallocHeap, sharedIntlData) \
|
||||
macro(_, MallocHeap, uncompressedSourceCache) \
|
||||
macro(_, MallocHeap, scriptData)
|
||||
|
||||
RuntimeSizes()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
scriptSourceInfo(),
|
||||
code(),
|
||||
gc(),
|
||||
notableScriptSources()
|
||||
{
|
||||
allScriptSources = js_new<ScriptSourcesHashMap>();
|
||||
if (!allScriptSources || !allScriptSources->init())
|
||||
MOZ_CRASH("oom");
|
||||
}
|
||||
|
||||
~RuntimeSizes() {
|
||||
// |allScriptSources| is usually deleted and set to nullptr before this
|
||||
// destructor runs. But there are failure cases due to OOMs that may
|
||||
// prevent that, so it doesn't hurt to try again here.
|
||||
js_delete(allScriptSources);
|
||||
}
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
scriptSourceInfo.addToServoSizes(sizes);
|
||||
code.addToServoSizes(sizes);
|
||||
gc.addToServoSizes(sizes);
|
||||
}
|
||||
|
||||
// The script source measurements in |scriptSourceInfo| are initially for
|
||||
// all script sources. At the end, if the measurement granularity is
|
||||
// FineGrained, we subtract the measurements of the notable script sources
|
||||
// and move them into |notableScriptSources|.
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
ScriptSourceInfo scriptSourceInfo;
|
||||
CodeSizes code;
|
||||
GCSizes gc;
|
||||
|
||||
typedef js::HashMap<const char*, ScriptSourceInfo,
|
||||
js::CStringHashPolicy,
|
||||
js::SystemAllocPolicy> ScriptSourcesHashMap;
|
||||
|
||||
// |allScriptSources| is only used transiently. During the reporting phase
|
||||
// it is filled with info about every script source in the runtime. It's
|
||||
// then used to fill in |notableScriptSources| (which actually gets
|
||||
// reported), and immediately discarded afterwards.
|
||||
ScriptSourcesHashMap* allScriptSources;
|
||||
js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources;
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
struct UnusedGCThingSizes
|
||||
{
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(Other, GCHeapUnused, object) \
|
||||
macro(Other, GCHeapUnused, script) \
|
||||
macro(Other, GCHeapUnused, lazyScript) \
|
||||
macro(Other, GCHeapUnused, shape) \
|
||||
macro(Other, GCHeapUnused, baseShape) \
|
||||
macro(Other, GCHeapUnused, objectGroup) \
|
||||
macro(Other, GCHeapUnused, string) \
|
||||
macro(Other, GCHeapUnused, symbol) \
|
||||
macro(Other, GCHeapUnused, jitcode) \
|
||||
macro(Other, GCHeapUnused, scope)
|
||||
|
||||
UnusedGCThingSizes()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
dummy()
|
||||
{}
|
||||
|
||||
UnusedGCThingSizes(UnusedGCThingSizes&& other)
|
||||
: FOR_EACH_SIZE(COPY_OTHER_SIZE)
|
||||
dummy()
|
||||
{}
|
||||
|
||||
void addToKind(JS::TraceKind kind, intptr_t n) {
|
||||
switch (kind) {
|
||||
case JS::TraceKind::Object: object += n; break;
|
||||
case JS::TraceKind::String: string += n; break;
|
||||
case JS::TraceKind::Symbol: symbol += n; break;
|
||||
case JS::TraceKind::Script: script += n; break;
|
||||
case JS::TraceKind::Shape: shape += n; break;
|
||||
case JS::TraceKind::BaseShape: baseShape += n; break;
|
||||
case JS::TraceKind::JitCode: jitcode += n; break;
|
||||
case JS::TraceKind::LazyScript: lazyScript += n; break;
|
||||
case JS::TraceKind::ObjectGroup: objectGroup += n; break;
|
||||
case JS::TraceKind::Scope: scope += n; break;
|
||||
default:
|
||||
MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
|
||||
}
|
||||
}
|
||||
|
||||
void addSizes(const UnusedGCThingSizes& other) {
|
||||
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
||||
}
|
||||
|
||||
size_t totalSize() const {
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N)
|
||||
return n;
|
||||
}
|
||||
|
||||
void addToTabSizes(JS::TabSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
|
||||
}
|
||||
|
||||
void addToServoSizes(JS::ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
}
|
||||
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
struct ZoneStats
|
||||
{
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(Other, GCHeapUsed, symbolsGCHeap) \
|
||||
macro(Other, GCHeapAdmin, gcHeapArenaAdmin) \
|
||||
macro(Other, GCHeapUsed, lazyScriptsGCHeap) \
|
||||
macro(Other, MallocHeap, lazyScriptsMallocHeap) \
|
||||
macro(Other, GCHeapUsed, jitCodesGCHeap) \
|
||||
macro(Other, GCHeapUsed, objectGroupsGCHeap) \
|
||||
macro(Other, MallocHeap, objectGroupsMallocHeap) \
|
||||
macro(Other, GCHeapUsed, scopesGCHeap) \
|
||||
macro(Other, MallocHeap, scopesMallocHeap) \
|
||||
macro(Other, MallocHeap, typePool) \
|
||||
macro(Other, MallocHeap, baselineStubsOptimized) \
|
||||
macro(Other, MallocHeap, uniqueIdMap) \
|
||||
macro(Other, MallocHeap, shapeTables)
|
||||
|
||||
ZoneStats()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
unusedGCThings(),
|
||||
stringInfo(),
|
||||
shapeInfo(),
|
||||
extra(),
|
||||
allStrings(nullptr),
|
||||
notableStrings(),
|
||||
isTotals(true)
|
||||
{}
|
||||
|
||||
ZoneStats(ZoneStats&& other)
|
||||
: FOR_EACH_SIZE(COPY_OTHER_SIZE)
|
||||
unusedGCThings(mozilla::Move(other.unusedGCThings)),
|
||||
stringInfo(mozilla::Move(other.stringInfo)),
|
||||
shapeInfo(mozilla::Move(other.shapeInfo)),
|
||||
extra(other.extra),
|
||||
allStrings(other.allStrings),
|
||||
notableStrings(mozilla::Move(other.notableStrings)),
|
||||
isTotals(other.isTotals)
|
||||
{
|
||||
other.allStrings = nullptr;
|
||||
MOZ_ASSERT(!other.isTotals);
|
||||
}
|
||||
|
||||
~ZoneStats() {
|
||||
// |allStrings| is usually deleted and set to nullptr before this
|
||||
// destructor runs. But there are failure cases due to OOMs that may
|
||||
// prevent that, so it doesn't hurt to try again here.
|
||||
js_delete(allStrings);
|
||||
}
|
||||
|
||||
bool initStrings(JSRuntime* rt);
|
||||
|
||||
void addSizes(const ZoneStats& other) {
|
||||
MOZ_ASSERT(isTotals);
|
||||
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
||||
unusedGCThings.addSizes(other.unusedGCThings);
|
||||
stringInfo.add(other.stringInfo);
|
||||
shapeInfo.add(other.shapeInfo);
|
||||
}
|
||||
|
||||
size_t sizeOfLiveGCThings() const {
|
||||
MOZ_ASSERT(isTotals);
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
|
||||
n += stringInfo.sizeOfLiveGCThings();
|
||||
n += shapeInfo.sizeOfLiveGCThings();
|
||||
return n;
|
||||
}
|
||||
|
||||
void addToTabSizes(JS::TabSizes* sizes) const {
|
||||
MOZ_ASSERT(isTotals);
|
||||
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
|
||||
unusedGCThings.addToTabSizes(sizes);
|
||||
stringInfo.addToTabSizes(sizes);
|
||||
shapeInfo.addToTabSizes(sizes);
|
||||
}
|
||||
|
||||
void addToServoSizes(JS::ServoSizes *sizes) const {
|
||||
MOZ_ASSERT(isTotals);
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
unusedGCThings.addToServoSizes(sizes);
|
||||
stringInfo.addToServoSizes(sizes);
|
||||
shapeInfo.addToServoSizes(sizes);
|
||||
}
|
||||
|
||||
// These string measurements are initially for all strings. At the end,
|
||||
// if the measurement granularity is FineGrained, we subtract the
|
||||
// measurements of the notable script sources and move them into
|
||||
// |notableStrings|.
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
UnusedGCThingSizes unusedGCThings;
|
||||
StringInfo stringInfo;
|
||||
ShapeInfo shapeInfo;
|
||||
void* extra; // This field can be used by embedders.
|
||||
|
||||
typedef js::HashMap<JSString*, StringInfo,
|
||||
js::InefficientNonFlatteningStringHashPolicy,
|
||||
js::SystemAllocPolicy> StringsHashMap;
|
||||
|
||||
// |allStrings| is only used transiently. During the zone traversal it is
|
||||
// filled with info about every string in the zone. It's then used to fill
|
||||
// in |notableStrings| (which actually gets reported), and immediately
|
||||
// discarded afterwards.
|
||||
StringsHashMap* allStrings;
|
||||
js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
|
||||
bool isTotals;
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
struct CompartmentStats
|
||||
{
|
||||
// We assume that |objectsPrivate| is on the malloc heap, but it's not
|
||||
// actually guaranteed. But for Servo, at least, it's a moot point because
|
||||
// it doesn't provide an ObjectPrivateVisitor so the value will always be
|
||||
// zero.
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(Private, MallocHeap, objectsPrivate) \
|
||||
macro(Other, GCHeapUsed, scriptsGCHeap) \
|
||||
macro(Other, MallocHeap, scriptsMallocHeapData) \
|
||||
macro(Other, MallocHeap, baselineData) \
|
||||
macro(Other, MallocHeap, baselineStubsFallback) \
|
||||
macro(Other, MallocHeap, ionData) \
|
||||
macro(Other, MallocHeap, typeInferenceTypeScripts) \
|
||||
macro(Other, MallocHeap, typeInferenceAllocationSiteTables) \
|
||||
macro(Other, MallocHeap, typeInferenceArrayTypeTables) \
|
||||
macro(Other, MallocHeap, typeInferenceObjectTypeTables) \
|
||||
macro(Other, MallocHeap, compartmentObject) \
|
||||
macro(Other, MallocHeap, compartmentTables) \
|
||||
macro(Other, MallocHeap, innerViewsTable) \
|
||||
macro(Other, MallocHeap, lazyArrayBuffersTable) \
|
||||
macro(Other, MallocHeap, objectMetadataTable) \
|
||||
macro(Other, MallocHeap, crossCompartmentWrappersTable) \
|
||||
macro(Other, MallocHeap, regexpCompartment) \
|
||||
macro(Other, MallocHeap, savedStacksSet) \
|
||||
macro(Other, MallocHeap, varNamesSet) \
|
||||
macro(Other, MallocHeap, nonSyntacticLexicalScopesTable) \
|
||||
macro(Other, MallocHeap, jitCompartment) \
|
||||
macro(Other, MallocHeap, privateData)
|
||||
|
||||
CompartmentStats()
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
classInfo(),
|
||||
extra(),
|
||||
allClasses(nullptr),
|
||||
notableClasses(),
|
||||
isTotals(true)
|
||||
{}
|
||||
|
||||
CompartmentStats(CompartmentStats&& other)
|
||||
: FOR_EACH_SIZE(COPY_OTHER_SIZE)
|
||||
classInfo(mozilla::Move(other.classInfo)),
|
||||
extra(other.extra),
|
||||
allClasses(other.allClasses),
|
||||
notableClasses(mozilla::Move(other.notableClasses)),
|
||||
isTotals(other.isTotals)
|
||||
{
|
||||
other.allClasses = nullptr;
|
||||
MOZ_ASSERT(!other.isTotals);
|
||||
}
|
||||
|
||||
CompartmentStats(const CompartmentStats&) = delete; // disallow copying
|
||||
|
||||
~CompartmentStats() {
|
||||
// |allClasses| is usually deleted and set to nullptr before this
|
||||
// destructor runs. But there are failure cases due to OOMs that may
|
||||
// prevent that, so it doesn't hurt to try again here.
|
||||
js_delete(allClasses);
|
||||
}
|
||||
|
||||
bool initClasses(JSRuntime* rt);
|
||||
|
||||
void addSizes(const CompartmentStats& other) {
|
||||
MOZ_ASSERT(isTotals);
|
||||
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
||||
classInfo.add(other.classInfo);
|
||||
}
|
||||
|
||||
size_t sizeOfLiveGCThings() const {
|
||||
MOZ_ASSERT(isTotals);
|
||||
size_t n = 0;
|
||||
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
|
||||
n += classInfo.sizeOfLiveGCThings();
|
||||
return n;
|
||||
}
|
||||
|
||||
void addToTabSizes(TabSizes* sizes) const {
|
||||
MOZ_ASSERT(isTotals);
|
||||
FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
|
||||
classInfo.addToTabSizes(sizes);
|
||||
}
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
MOZ_ASSERT(isTotals);
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
|
||||
classInfo.addToServoSizes(sizes);
|
||||
}
|
||||
|
||||
// The class measurements in |classInfo| are initially for all classes. At
|
||||
// the end, if the measurement granularity is FineGrained, we subtract the
|
||||
// measurements of the notable classes and move them into |notableClasses|.
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
ClassInfo classInfo;
|
||||
void* extra; // This field can be used by embedders.
|
||||
|
||||
typedef js::HashMap<const char*, ClassInfo,
|
||||
js::CStringHashPolicy,
|
||||
js::SystemAllocPolicy> ClassesHashMap;
|
||||
|
||||
// These are similar to |allStrings| and |notableStrings| in ZoneStats.
|
||||
ClassesHashMap* allClasses;
|
||||
js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
|
||||
bool isTotals;
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
typedef js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> CompartmentStatsVector;
|
||||
typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
|
||||
|
||||
struct RuntimeStats
|
||||
{
|
||||
// |gcHeapChunkTotal| is ignored because it's the sum of all the other
|
||||
// values. |gcHeapGCThings| is ignored because it's the sum of some of the
|
||||
// values from the zones and compartments. Both of those values are not
|
||||
// reported directly, but are just present for sanity-checking other
|
||||
// values.
|
||||
#define FOR_EACH_SIZE(macro) \
|
||||
macro(_, Ignore, gcHeapChunkTotal) \
|
||||
macro(_, GCHeapDecommitted, gcHeapDecommittedArenas) \
|
||||
macro(_, GCHeapUnused, gcHeapUnusedChunks) \
|
||||
macro(_, GCHeapUnused, gcHeapUnusedArenas) \
|
||||
macro(_, GCHeapAdmin, gcHeapChunkAdmin) \
|
||||
macro(_, Ignore, gcHeapGCThings)
|
||||
|
||||
explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
|
||||
: FOR_EACH_SIZE(ZERO_SIZE)
|
||||
runtime(),
|
||||
cTotals(),
|
||||
zTotals(),
|
||||
compartmentStatsVector(),
|
||||
zoneStatsVector(),
|
||||
currZoneStats(nullptr),
|
||||
mallocSizeOf_(mallocSizeOf)
|
||||
{}
|
||||
|
||||
// Here's a useful breakdown of the GC heap.
|
||||
//
|
||||
// - rtStats.gcHeapChunkTotal
|
||||
// - decommitted bytes
|
||||
// - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks)
|
||||
// - unused bytes
|
||||
// - rtStats.gcHeapUnusedChunks (empty chunks)
|
||||
// - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
|
||||
// - rtStats.zTotals.unusedGCThings.totalSize() (empty GC thing slots within non-empty arenas)
|
||||
// - used bytes
|
||||
// - rtStats.gcHeapChunkAdmin
|
||||
// - rtStats.zTotals.gcHeapArenaAdmin
|
||||
// - rtStats.gcHeapGCThings (in-use GC things)
|
||||
// == rtStats.zTotals.sizeOfLiveGCThings() + rtStats.cTotals.sizeOfLiveGCThings()
|
||||
//
|
||||
// It's possible that some arenas in empty chunks may be decommitted, but
|
||||
// we don't count those under rtStats.gcHeapDecommittedArenas because (a)
|
||||
// it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
|
||||
// multiple of the chunk size, which is good.
|
||||
|
||||
void addToServoSizes(ServoSizes *sizes) const {
|
||||
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
|
||||
runtime.addToServoSizes(sizes);
|
||||
}
|
||||
|
||||
FOR_EACH_SIZE(DECL_SIZE)
|
||||
|
||||
RuntimeSizes runtime;
|
||||
|
||||
CompartmentStats cTotals; // The sum of this runtime's compartments' measurements.
|
||||
ZoneStats zTotals; // The sum of this runtime's zones' measurements.
|
||||
|
||||
CompartmentStatsVector compartmentStatsVector;
|
||||
ZoneStatsVector zoneStatsVector;
|
||||
|
||||
ZoneStats* currZoneStats;
|
||||
|
||||
mozilla::MallocSizeOf mallocSizeOf_;
|
||||
|
||||
virtual void initExtraCompartmentStats(JSCompartment* c, CompartmentStats* cstats) = 0;
|
||||
virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats) = 0;
|
||||
|
||||
#undef FOR_EACH_SIZE
|
||||
};
|
||||
|
||||
class ObjectPrivateVisitor
|
||||
{
|
||||
public:
|
||||
// Within CollectRuntimeStats, this method is called for each JS object
|
||||
// that has an nsISupports pointer.
|
||||
virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0;
|
||||
|
||||
// A callback that gets a JSObject's nsISupports pointer, if it has one.
|
||||
// Note: this function does *not* addref |iface|.
|
||||
typedef bool(*GetISupportsFun)(JSObject* obj, nsISupports** iface);
|
||||
GetISupportsFun getISupports_;
|
||||
|
||||
explicit ObjectPrivateVisitor(GetISupportsFun getISupports)
|
||||
: getISupports_(getISupports)
|
||||
{}
|
||||
};
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
CollectRuntimeStats(JSContext* cx, RuntimeStats* rtStats, ObjectPrivateVisitor* opv, bool anonymize);
|
||||
|
||||
extern JS_PUBLIC_API(size_t)
|
||||
SystemCompartmentCount(JSContext* cx);
|
||||
|
||||
extern JS_PUBLIC_API(size_t)
|
||||
UserCompartmentCount(JSContext* cx);
|
||||
|
||||
extern JS_PUBLIC_API(size_t)
|
||||
PeakSizeOfTemporary(const JSContext* cx);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
AddSizeOfTab(JSContext* cx, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf,
|
||||
ObjectPrivateVisitor* opv, TabSizes* sizes);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
AddServoSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf,
|
||||
ObjectPrivateVisitor *opv, ServoSizes *sizes);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#undef DECL_SIZE
|
||||
#undef ZERO_SIZE
|
||||
#undef COPY_OTHER_SIZE
|
||||
#undef ADD_OTHER_SIZE
|
||||
#undef SUB_OTHER_SIZE
|
||||
#undef ADD_SIZE_TO_N
|
||||
#undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
|
||||
#undef ADD_TO_TAB_SIZES
|
||||
|
||||
#endif /* js_MemoryMetrics_h */
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* JSPrincipals and related interfaces. */
|
||||
|
||||
#ifndef js_Principals_h
|
||||
#define js_Principals_h
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
|
||||
namespace js {
|
||||
struct PerformanceGroup;
|
||||
} // namespace js
|
||||
|
||||
struct JSPrincipals {
|
||||
/* Don't call "destroy"; use reference counting macros below. */
|
||||
mozilla::Atomic<int32_t> refcount;
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
/* A helper to facilitate principals debugging. */
|
||||
uint32_t debugToken;
|
||||
#endif
|
||||
|
||||
JSPrincipals() : refcount(0) {}
|
||||
|
||||
void setDebugToken(uint32_t token) {
|
||||
# ifdef JS_DEBUG
|
||||
debugToken = token;
|
||||
# endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the principals with the given |writer|. Return false on failure,
|
||||
* true on success.
|
||||
*/
|
||||
virtual bool write(JSContext* cx, JSStructuredCloneWriter* writer) = 0;
|
||||
|
||||
/*
|
||||
* This is not defined by the JS engine but should be provided by the
|
||||
* embedding.
|
||||
*/
|
||||
JS_PUBLIC_API(void) dump();
|
||||
};
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_HoldPrincipals(JSPrincipals* principals);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_DropPrincipals(JSContext* cx, JSPrincipals* principals);
|
||||
|
||||
// Return whether the first principal subsumes the second. The exact meaning of
|
||||
// 'subsumes' is left up to the browser. Subsumption is checked inside the JS
|
||||
// engine when determining, e.g., which stack frames to display in a backtrace.
|
||||
typedef bool
|
||||
(* JSSubsumesOp)(JSPrincipals* first, JSPrincipals* second);
|
||||
|
||||
/*
|
||||
* Used to check if a CSP instance wants to disable eval() and friends.
|
||||
* See js_CheckCSPPermitsJSAction() in jsobj.
|
||||
*/
|
||||
typedef bool
|
||||
(* JSCSPEvalChecker)(JSContext* cx);
|
||||
|
||||
struct JSSecurityCallbacks {
|
||||
JSCSPEvalChecker contentSecurityPolicyAllows;
|
||||
JSSubsumesOp subsumes;
|
||||
};
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetSecurityCallbacks(JSContext* cx, const JSSecurityCallbacks* callbacks);
|
||||
|
||||
extern JS_PUBLIC_API(const JSSecurityCallbacks*)
|
||||
JS_GetSecurityCallbacks(JSContext* cx);
|
||||
|
||||
/*
|
||||
* Code running with "trusted" principals will be given a deeper stack
|
||||
* allocation than ordinary scripts. This allows trusted script to run after
|
||||
* untrusted script has exhausted the stack. This function sets the
|
||||
* runtime-wide trusted principal.
|
||||
*
|
||||
* This principals is not held (via JS_HoldPrincipals/JS_DropPrincipals).
|
||||
* Instead, the caller must ensure that the given principals stays valid for as
|
||||
* long as 'cx' may point to it. If the principals would be destroyed before
|
||||
* 'cx', JS_SetTrustedPrincipals must be called again, passing nullptr for
|
||||
* 'prin'.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetTrustedPrincipals(JSContext* cx, JSPrincipals* prin);
|
||||
|
||||
typedef void
|
||||
(* JSDestroyPrincipalsOp)(JSPrincipals* principals);
|
||||
|
||||
/*
|
||||
* Initialize the callback that is called to destroy JSPrincipals instance
|
||||
* when its reference counter drops to zero. The initialization can be done
|
||||
* only once per JS runtime.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_InitDestroyPrincipalsCallback(JSContext* cx, JSDestroyPrincipalsOp destroyPrincipals);
|
||||
|
||||
/*
|
||||
* Read a JSPrincipals instance from the given |reader| and initialize the out
|
||||
* paratemer |outPrincipals| to the JSPrincipals instance read.
|
||||
*
|
||||
* Return false on failure, true on success. The |outPrincipals| parameter
|
||||
* should not be modified if false is returned.
|
||||
*
|
||||
* The caller is not responsible for calling JS_HoldPrincipals on the resulting
|
||||
* JSPrincipals instance, the JSReadPrincipalsOp must increment the refcount of
|
||||
* the resulting JSPrincipals on behalf of the caller.
|
||||
*/
|
||||
using JSReadPrincipalsOp = bool (*)(JSContext* cx, JSStructuredCloneReader* reader,
|
||||
JSPrincipals** outPrincipals);
|
||||
|
||||
/*
|
||||
* Initialize the callback that is called to read JSPrincipals instances from a
|
||||
* buffer. The initialization can be done only once per JS runtime.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_InitReadPrincipalsCallback(JSContext* cx, JSReadPrincipalsOp read);
|
||||
|
||||
|
||||
#endif /* js_Principals_h */
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_ProfilingFrameIterator_h
|
||||
#define js_ProfilingFrameIterator_h
|
||||
|
||||
#include "mozilla/Alignment.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jsbytecode.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
struct JSContext;
|
||||
struct JSRuntime;
|
||||
class JSScript;
|
||||
|
||||
namespace js {
|
||||
class Activation;
|
||||
namespace jit {
|
||||
class JitActivation;
|
||||
class JitProfilingFrameIterator;
|
||||
class JitcodeGlobalEntry;
|
||||
} // namespace jit
|
||||
namespace wasm {
|
||||
class ProfilingFrameIterator;
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
struct ForEachTrackedOptimizationAttemptOp;
|
||||
struct ForEachTrackedOptimizationTypeInfoOp;
|
||||
|
||||
// This iterator can be used to walk the stack of a thread suspended at an
|
||||
// arbitrary pc. To provide acurate results, profiling must have been enabled
|
||||
// (via EnableRuntimeProfilingStack) before executing the callstack being
|
||||
// unwound.
|
||||
//
|
||||
// Note that the caller must not do anything that could cause GC to happen while
|
||||
// the iterator is alive, since this could invalidate Ion code and cause its
|
||||
// contents to become out of date.
|
||||
class JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
{
|
||||
JSRuntime* rt_;
|
||||
uint32_t sampleBufferGen_;
|
||||
js::Activation* activation_;
|
||||
|
||||
// When moving past a JitActivation, we need to save the prevJitTop
|
||||
// from it to use as the exit-frame pointer when the next caller jit
|
||||
// activation (if any) comes around.
|
||||
void* savedPrevJitTop_;
|
||||
|
||||
JS::AutoCheckCannotGC nogc_;
|
||||
|
||||
static const unsigned StorageSpace = 8 * sizeof(void*);
|
||||
mozilla::AlignedStorage<StorageSpace> storage_;
|
||||
js::wasm::ProfilingFrameIterator& wasmIter() {
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(isWasm());
|
||||
return *reinterpret_cast<js::wasm::ProfilingFrameIterator*>(storage_.addr());
|
||||
}
|
||||
const js::wasm::ProfilingFrameIterator& wasmIter() const {
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(isWasm());
|
||||
return *reinterpret_cast<const js::wasm::ProfilingFrameIterator*>(storage_.addr());
|
||||
}
|
||||
|
||||
js::jit::JitProfilingFrameIterator& jitIter() {
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(isJit());
|
||||
return *reinterpret_cast<js::jit::JitProfilingFrameIterator*>(storage_.addr());
|
||||
}
|
||||
|
||||
const js::jit::JitProfilingFrameIterator& jitIter() const {
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(isJit());
|
||||
return *reinterpret_cast<const js::jit::JitProfilingFrameIterator*>(storage_.addr());
|
||||
}
|
||||
|
||||
void settle();
|
||||
|
||||
bool hasSampleBufferGen() const {
|
||||
return sampleBufferGen_ != UINT32_MAX;
|
||||
}
|
||||
|
||||
public:
|
||||
struct RegisterState
|
||||
{
|
||||
RegisterState() : pc(nullptr), sp(nullptr), lr(nullptr) {}
|
||||
void* pc;
|
||||
void* sp;
|
||||
void* lr;
|
||||
};
|
||||
|
||||
ProfilingFrameIterator(JSContext* cx, const RegisterState& state,
|
||||
uint32_t sampleBufferGen = UINT32_MAX);
|
||||
~ProfilingFrameIterator();
|
||||
void operator++();
|
||||
bool done() const { return !activation_; }
|
||||
|
||||
// Assuming the stack grows down (we do), the return value:
|
||||
// - always points into the stack
|
||||
// - is weakly monotonically increasing (may be equal for successive frames)
|
||||
// - will compare greater than newer native and psuedo-stack frame addresses
|
||||
// and less than older native and psuedo-stack frame addresses
|
||||
void* stackAddress() const;
|
||||
|
||||
enum FrameKind
|
||||
{
|
||||
Frame_Baseline,
|
||||
Frame_Ion,
|
||||
Frame_Wasm
|
||||
};
|
||||
|
||||
struct Frame
|
||||
{
|
||||
FrameKind kind;
|
||||
void* stackAddress;
|
||||
void* returnAddress;
|
||||
void* activation;
|
||||
UniqueChars label;
|
||||
};
|
||||
|
||||
bool isWasm() const;
|
||||
bool isJit() const;
|
||||
|
||||
uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const;
|
||||
|
||||
mozilla::Maybe<Frame> getPhysicalFrameWithoutLabel() const;
|
||||
|
||||
private:
|
||||
mozilla::Maybe<Frame> getPhysicalFrameAndEntry(js::jit::JitcodeGlobalEntry* entry) const;
|
||||
|
||||
void iteratorConstruct(const RegisterState& state);
|
||||
void iteratorConstruct();
|
||||
void iteratorDestroy();
|
||||
bool iteratorDone();
|
||||
};
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
IsProfilingEnabledForContext(JSContext* cx);
|
||||
|
||||
/**
|
||||
* After each sample run, this method should be called with the latest sample
|
||||
* buffer generation, and the lapCount. It will update corresponding fields on
|
||||
* JSRuntime.
|
||||
*
|
||||
* See fields |profilerSampleBufferGen|, |profilerSampleBufferLapCount| on
|
||||
* JSRuntime for documentation about what these values are used for.
|
||||
*/
|
||||
JS_FRIEND_API(void)
|
||||
UpdateJSContextProfilerSampleBufferGen(JSContext* cx, uint32_t generation,
|
||||
uint32_t lapCount);
|
||||
|
||||
struct ForEachProfiledFrameOp
|
||||
{
|
||||
// A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
|
||||
// lookups on JitcodeGlobalTable.
|
||||
class MOZ_STACK_CLASS FrameHandle
|
||||
{
|
||||
friend JS_PUBLIC_API(void) ForEachProfiledFrame(JSContext* cx, void* addr,
|
||||
ForEachProfiledFrameOp& op);
|
||||
|
||||
JSRuntime* rt_;
|
||||
js::jit::JitcodeGlobalEntry& entry_;
|
||||
void* addr_;
|
||||
void* canonicalAddr_;
|
||||
const char* label_;
|
||||
uint32_t depth_;
|
||||
mozilla::Maybe<uint8_t> optsIndex_;
|
||||
|
||||
FrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, void* addr,
|
||||
const char* label, uint32_t depth);
|
||||
|
||||
void updateHasTrackedOptimizations();
|
||||
|
||||
public:
|
||||
const char* label() const { return label_; }
|
||||
uint32_t depth() const { return depth_; }
|
||||
bool hasTrackedOptimizations() const { return optsIndex_.isSome(); }
|
||||
void* canonicalAddress() const { return canonicalAddr_; }
|
||||
|
||||
ProfilingFrameIterator::FrameKind frameKind() const;
|
||||
void forEachOptimizationAttempt(ForEachTrackedOptimizationAttemptOp& op,
|
||||
JSScript** scriptOut, jsbytecode** pcOut) const;
|
||||
void forEachOptimizationTypeInfo(ForEachTrackedOptimizationTypeInfoOp& op) const;
|
||||
};
|
||||
|
||||
// Called once per frame.
|
||||
virtual void operator()(const FrameHandle& frame) = 0;
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
ForEachProfiledFrame(JSContext* cx, void* addr, ForEachProfiledFrameOp& op);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_ProfilingFrameIterator_h */
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_ProfilingStack_h
|
||||
#define js_ProfilingStack_h
|
||||
|
||||
#include "jsbytecode.h"
|
||||
#include "jstypes.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
#include "js/Utility.h"
|
||||
|
||||
struct JSRuntime;
|
||||
class JSTracer;
|
||||
|
||||
namespace js {
|
||||
|
||||
// A call stack can be specified to the JS engine such that all JS entry/exits
|
||||
// to functions push/pop an entry to/from the specified stack.
|
||||
//
|
||||
// For more detailed information, see vm/SPSProfiler.h.
|
||||
//
|
||||
class ProfileEntry
|
||||
{
|
||||
// All fields are marked volatile to prevent the compiler from re-ordering
|
||||
// instructions. Namely this sequence:
|
||||
//
|
||||
// entry[size] = ...;
|
||||
// size++;
|
||||
//
|
||||
// If the size modification were somehow reordered before the stores, then
|
||||
// if a sample were taken it would be examining bogus information.
|
||||
//
|
||||
// A ProfileEntry represents both a C++ profile entry and a JS one.
|
||||
|
||||
// Descriptive string of this entry.
|
||||
const char * volatile string;
|
||||
|
||||
// Stack pointer for non-JS entries, the script pointer otherwise.
|
||||
void * volatile spOrScript;
|
||||
|
||||
// Line number for non-JS entries, the bytecode offset otherwise.
|
||||
int32_t volatile lineOrPcOffset;
|
||||
|
||||
// General purpose storage describing this frame.
|
||||
uint32_t volatile flags_;
|
||||
|
||||
public:
|
||||
// These traits are bit masks. Make sure they're powers of 2.
|
||||
enum Flags : uint32_t {
|
||||
// Indicate whether a profile entry represents a CPP frame. If not set,
|
||||
// a JS frame is assumed by default. You're not allowed to publicly
|
||||
// change the frame type. Instead, initialize the ProfileEntry as either
|
||||
// a JS or CPP frame with `initJsFrame` or `initCppFrame` respectively.
|
||||
IS_CPP_ENTRY = 0x01,
|
||||
|
||||
// Indicate that copying the frame label is not necessary when taking a
|
||||
// sample of the pseudostack.
|
||||
FRAME_LABEL_COPY = 0x02,
|
||||
|
||||
// This ProfileEntry is a dummy entry indicating the start of a run
|
||||
// of JS pseudostack entries.
|
||||
BEGIN_PSEUDO_JS = 0x04,
|
||||
|
||||
// This flag is used to indicate that an interpreter JS entry has OSR-ed
|
||||
// into baseline.
|
||||
OSR = 0x08,
|
||||
|
||||
// Union of all flags.
|
||||
ALL = IS_CPP_ENTRY|FRAME_LABEL_COPY|BEGIN_PSEUDO_JS|OSR,
|
||||
|
||||
// Mask for removing all flags except the category information.
|
||||
CATEGORY_MASK = ~ALL
|
||||
};
|
||||
|
||||
// Keep these in sync with devtools/client/performance/modules/categories.js
|
||||
enum class Category : uint32_t {
|
||||
OTHER = 0x10,
|
||||
CSS = 0x20,
|
||||
JS = 0x40,
|
||||
GC = 0x80,
|
||||
CC = 0x100,
|
||||
NETWORK = 0x200,
|
||||
GRAPHICS = 0x400,
|
||||
STORAGE = 0x800,
|
||||
EVENTS = 0x1000,
|
||||
|
||||
FIRST = OTHER,
|
||||
LAST = EVENTS
|
||||
};
|
||||
|
||||
static_assert((static_cast<int>(Category::FIRST) & Flags::ALL) == 0,
|
||||
"The category bitflags should not intersect with the other flags!");
|
||||
|
||||
// All of these methods are marked with the 'volatile' keyword because SPS's
|
||||
// representation of the stack is stored such that all ProfileEntry
|
||||
// instances are volatile. These methods would not be available unless they
|
||||
// were marked as volatile as well.
|
||||
|
||||
bool isCpp() const volatile { return hasFlag(IS_CPP_ENTRY); }
|
||||
bool isJs() const volatile { return !isCpp(); }
|
||||
|
||||
bool isCopyLabel() const volatile { return hasFlag(FRAME_LABEL_COPY); }
|
||||
|
||||
void setLabel(const char* aString) volatile { string = aString; }
|
||||
const char* label() const volatile { return string; }
|
||||
|
||||
void initJsFrame(JSScript* aScript, jsbytecode* aPc) volatile {
|
||||
flags_ = 0;
|
||||
spOrScript = aScript;
|
||||
setPC(aPc);
|
||||
}
|
||||
void initCppFrame(void* aSp, uint32_t aLine) volatile {
|
||||
flags_ = IS_CPP_ENTRY;
|
||||
spOrScript = aSp;
|
||||
lineOrPcOffset = static_cast<int32_t>(aLine);
|
||||
}
|
||||
|
||||
void setFlag(uint32_t flag) volatile {
|
||||
MOZ_ASSERT(flag != IS_CPP_ENTRY);
|
||||
flags_ |= flag;
|
||||
}
|
||||
void unsetFlag(uint32_t flag) volatile {
|
||||
MOZ_ASSERT(flag != IS_CPP_ENTRY);
|
||||
flags_ &= ~flag;
|
||||
}
|
||||
bool hasFlag(uint32_t flag) const volatile {
|
||||
return bool(flags_ & flag);
|
||||
}
|
||||
|
||||
uint32_t flags() const volatile {
|
||||
return flags_;
|
||||
}
|
||||
|
||||
uint32_t category() const volatile {
|
||||
return flags_ & CATEGORY_MASK;
|
||||
}
|
||||
void setCategory(Category c) volatile {
|
||||
MOZ_ASSERT(c >= Category::FIRST);
|
||||
MOZ_ASSERT(c <= Category::LAST);
|
||||
flags_ &= ~CATEGORY_MASK;
|
||||
setFlag(static_cast<uint32_t>(c));
|
||||
}
|
||||
|
||||
void setOSR() volatile {
|
||||
MOZ_ASSERT(isJs());
|
||||
setFlag(OSR);
|
||||
}
|
||||
void unsetOSR() volatile {
|
||||
MOZ_ASSERT(isJs());
|
||||
unsetFlag(OSR);
|
||||
}
|
||||
bool isOSR() const volatile {
|
||||
return hasFlag(OSR);
|
||||
}
|
||||
|
||||
void* stackAddress() const volatile {
|
||||
MOZ_ASSERT(!isJs());
|
||||
return spOrScript;
|
||||
}
|
||||
JSScript* script() const volatile;
|
||||
uint32_t line() const volatile {
|
||||
MOZ_ASSERT(!isJs());
|
||||
return static_cast<uint32_t>(lineOrPcOffset);
|
||||
}
|
||||
|
||||
// Note that the pointer returned might be invalid.
|
||||
JSScript* rawScript() const volatile {
|
||||
MOZ_ASSERT(isJs());
|
||||
return (JSScript*)spOrScript;
|
||||
}
|
||||
|
||||
// We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp.
|
||||
JS_FRIEND_API(jsbytecode*) pc() const volatile;
|
||||
JS_FRIEND_API(void) setPC(jsbytecode* pc) volatile;
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
// The offset of a pc into a script's code can actually be 0, so to
|
||||
// signify a nullptr pc, use a -1 index. This is checked against in
|
||||
// pc() and setPC() to set/get the right pc.
|
||||
static const int32_t NullPCOffset = -1;
|
||||
|
||||
static size_t offsetOfLabel() { return offsetof(ProfileEntry, string); }
|
||||
static size_t offsetOfSpOrScript() { return offsetof(ProfileEntry, spOrScript); }
|
||||
static size_t offsetOfLineOrPcOffset() { return offsetof(ProfileEntry, lineOrPcOffset); }
|
||||
static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags_); }
|
||||
};
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
SetContextProfilingStack(JSContext* cx, ProfileEntry* stack, uint32_t* size,
|
||||
uint32_t max);
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
EnableContextProfilingStack(JSContext* cx, bool enabled);
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
|
||||
|
||||
JS_FRIEND_API(jsbytecode*)
|
||||
ProfilingGetPC(JSContext* cx, JSScript* script, void* ip);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* js_ProfilingStack_h */
|
||||
|
|
@ -1,632 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_Proxy_h
|
||||
#define js_Proxy_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "js/CallNonGenericMethod.h"
|
||||
#include "js/Class.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
using JS::AutoIdVector;
|
||||
using JS::CallArgs;
|
||||
using JS::Handle;
|
||||
using JS::HandleId;
|
||||
using JS::HandleObject;
|
||||
using JS::HandleValue;
|
||||
using JS::IsAcceptableThis;
|
||||
using JS::MutableHandle;
|
||||
using JS::MutableHandleObject;
|
||||
using JS::MutableHandleValue;
|
||||
using JS::NativeImpl;
|
||||
using JS::ObjectOpResult;
|
||||
using JS::PrivateValue;
|
||||
using JS::PropertyDescriptor;
|
||||
using JS::Value;
|
||||
|
||||
class RegExpGuard;
|
||||
class JS_FRIEND_API(Wrapper);
|
||||
|
||||
/*
|
||||
* A proxy is a JSObject with highly customizable behavior. ES6 specifies a
|
||||
* single kind of proxy, but the customization mechanisms we use to implement
|
||||
* ES6 Proxy objects are also useful wherever an object with weird behavior is
|
||||
* wanted. Proxies are used to implement:
|
||||
*
|
||||
* - the scope objects used by the Debugger's frame.eval() method
|
||||
* (see js::GetDebugScopeForFunction)
|
||||
*
|
||||
* - the khuey hack, whereby a whole compartment can be blown away
|
||||
* even if other compartments hold references to objects in it
|
||||
* (see js::NukeCrossCompartmentWrappers)
|
||||
*
|
||||
* - XPConnect security wrappers, which protect chrome from malicious content
|
||||
* (js/xpconnect/wrappers)
|
||||
*
|
||||
* - DOM objects with special property behavior, like named getters
|
||||
* (dom/bindings/Codegen.py generates these proxies from WebIDL)
|
||||
*
|
||||
* - semi-transparent use of objects that live in other processes
|
||||
* (CPOWs, implemented in js/ipc)
|
||||
*
|
||||
* ### Proxies and internal methods
|
||||
*
|
||||
* ES2016 specifies 13 internal methods. The runtime semantics of just
|
||||
* about everything a script can do to an object is specified in terms
|
||||
* of these internal methods. For example:
|
||||
*
|
||||
* JS code ES6 internal method that gets called
|
||||
* --------------------------- --------------------------------
|
||||
* obj.prop obj.[[Get]](obj, "prop")
|
||||
* "prop" in obj obj.[[HasProperty]]("prop")
|
||||
* new obj() obj.[[Construct]](<empty argument List>)
|
||||
*
|
||||
* With regard to the implementation of these internal methods, there are three
|
||||
* very different kinds of object in SpiderMonkey.
|
||||
*
|
||||
* 1. Native objects' internal methods are implemented in vm/NativeObject.cpp,
|
||||
* with duplicate (but functionally identical) implementations scattered
|
||||
* through the ICs and JITs.
|
||||
*
|
||||
* 2. Certain non-native objects have internal methods that are implemented as
|
||||
* magical js::ObjectOps hooks. We're trying to get rid of these.
|
||||
*
|
||||
* 3. All other objects are proxies. A proxy's internal methods are
|
||||
* implemented in C++, as the virtual methods of a C++ object stored on the
|
||||
* proxy, known as its handler.
|
||||
*
|
||||
* This means that just about anything you do to a proxy will end up going
|
||||
* through a C++ virtual method call. Possibly several. There's no reason the
|
||||
* JITs and ICs can't specialize for particular proxies, based on the handler;
|
||||
* but currently we don't do much of this, so the virtual method overhead
|
||||
* typically is actually incurred.
|
||||
*
|
||||
* ### The proxy handler hierarchy
|
||||
*
|
||||
* A major use case for proxies is to forward each internal method call to
|
||||
* another object, known as its target. The target can be an arbitrary JS
|
||||
* object. Not every proxy has the notion of a target, however.
|
||||
*
|
||||
* To minimize code duplication, a set of abstract proxy handler classes is
|
||||
* provided, from which other handlers may inherit. These abstract classes are
|
||||
* organized in the following hierarchy:
|
||||
*
|
||||
* BaseProxyHandler
|
||||
* |
|
||||
* Wrapper // has a target, can be unwrapped to reveal
|
||||
* | // target (see js::CheckedUnwrap)
|
||||
* |
|
||||
* CrossCompartmentWrapper // target is in another compartment;
|
||||
* // implements membrane between compartments
|
||||
*
|
||||
* Example: Some DOM objects (including all the arraylike DOM objects) are
|
||||
* implemented as proxies. Since these objects don't need to forward operations
|
||||
* to any underlying JS object, DOMJSProxyHandler directly subclasses
|
||||
* BaseProxyHandler.
|
||||
*
|
||||
* Gecko's security wrappers are examples of cross-compartment wrappers.
|
||||
*
|
||||
* ### Proxy prototype chains
|
||||
*
|
||||
* In addition to the normal methods, there are two models for proxy prototype
|
||||
* chains.
|
||||
*
|
||||
* 1. Proxies can use the standard prototype mechanism used throughout the
|
||||
* engine. To do so, simply pass a prototype to NewProxyObject() at
|
||||
* creation time. All prototype accesses will then "just work" to treat the
|
||||
* proxy as a "normal" object.
|
||||
*
|
||||
* 2. A proxy can implement more complicated prototype semantics (if, for
|
||||
* example, it wants to delegate the prototype lookup to a wrapped object)
|
||||
* by passing Proxy::LazyProto as the prototype at create time. This
|
||||
* guarantees that the getPrototype() handler method will be called every
|
||||
* time the object's prototype chain is accessed.
|
||||
*
|
||||
* This system is implemented with two methods: {get,set}Prototype. The
|
||||
* default implementation of setPrototype throws a TypeError. Since it is
|
||||
* not possible to create an object without a sense of prototype chain,
|
||||
* handlers must implement getPrototype if opting in to the dynamic
|
||||
* prototype system.
|
||||
*/
|
||||
|
||||
/*
|
||||
* BaseProxyHandler is the most generic kind of proxy handler. It does not make
|
||||
* any assumptions about the target. Consequently, it does not provide any
|
||||
* default implementation for most methods. As a convenience, a few high-level
|
||||
* methods, like get() and set(), are given default implementations that work by
|
||||
* calling the low-level methods, like getOwnPropertyDescriptor().
|
||||
*
|
||||
* Important: If you add a method here, you should probably also add a
|
||||
* Proxy::foo entry point with an AutoEnterPolicy. If you don't, you need an
|
||||
* explicit override for the method in SecurityWrapper. See bug 945826 comment 0.
|
||||
*/
|
||||
class JS_FRIEND_API(BaseProxyHandler)
|
||||
{
|
||||
/*
|
||||
* Sometimes it's desirable to designate groups of proxy handlers as "similar".
|
||||
* For this, we use the notion of a "family": A consumer-provided opaque pointer
|
||||
* that designates the larger group to which this proxy belongs.
|
||||
*
|
||||
* If it will never be important to differentiate this proxy from others as
|
||||
* part of a distinct group, nullptr may be used instead.
|
||||
*/
|
||||
const void* mFamily;
|
||||
|
||||
/*
|
||||
* Proxy handlers can use mHasPrototype to request the following special
|
||||
* treatment from the JS engine:
|
||||
*
|
||||
* - When mHasPrototype is true, the engine never calls these methods:
|
||||
* getPropertyDescriptor, has, set, enumerate, iterate. Instead, for
|
||||
* these operations, it calls the "own" methods like
|
||||
* getOwnPropertyDescriptor, hasOwn, defineProperty,
|
||||
* getOwnEnumerablePropertyKeys, etc., and consults the prototype chain
|
||||
* if needed.
|
||||
*
|
||||
* - When mHasPrototype is true, the engine calls handler->get() only if
|
||||
* handler->hasOwn() says an own property exists on the proxy. If not,
|
||||
* it consults the prototype chain.
|
||||
*
|
||||
* This is useful because it frees the ProxyHandler from having to implement
|
||||
* any behavior having to do with the prototype chain.
|
||||
*/
|
||||
bool mHasPrototype;
|
||||
|
||||
/*
|
||||
* All proxies indicate whether they have any sort of interesting security
|
||||
* policy that might prevent the caller from doing something it wants to
|
||||
* the object. In the case of wrappers, this distinction is used to
|
||||
* determine whether the caller may strip off the wrapper if it so desires.
|
||||
*/
|
||||
bool mHasSecurityPolicy;
|
||||
|
||||
public:
|
||||
explicit constexpr BaseProxyHandler(const void* aFamily, bool aHasPrototype = false,
|
||||
bool aHasSecurityPolicy = false)
|
||||
: mFamily(aFamily),
|
||||
mHasPrototype(aHasPrototype),
|
||||
mHasSecurityPolicy(aHasSecurityPolicy)
|
||||
{ }
|
||||
|
||||
bool hasPrototype() const {
|
||||
return mHasPrototype;
|
||||
}
|
||||
|
||||
bool hasSecurityPolicy() const {
|
||||
return mHasSecurityPolicy;
|
||||
}
|
||||
|
||||
inline const void* family() const {
|
||||
return mFamily;
|
||||
}
|
||||
static size_t offsetOfFamily() {
|
||||
return offsetof(BaseProxyHandler, mFamily);
|
||||
}
|
||||
|
||||
virtual bool finalizeInBackground(const Value& priv) const {
|
||||
/*
|
||||
* Called on creation of a proxy to determine whether its finalize
|
||||
* method can be finalized on the background thread.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool canNurseryAllocate() const {
|
||||
/*
|
||||
* Nursery allocation is allowed if and only if it is safe to not
|
||||
* run |finalize| when the ProxyObject dies.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Policy enforcement methods.
|
||||
*
|
||||
* enter() allows the policy to specify whether the caller may perform |act|
|
||||
* on the proxy's |id| property. In the case when |act| is CALL, |id| is
|
||||
* generally JSID_VOID.
|
||||
*
|
||||
* The |act| parameter to enter() specifies the action being performed.
|
||||
* If |bp| is false, the method suggests that the caller throw (though it
|
||||
* may still decide to squelch the error).
|
||||
*
|
||||
* We make these OR-able so that assertEnteredPolicy can pass a union of them.
|
||||
* For example, get{,Own}PropertyDescriptor is invoked by calls to ::get()
|
||||
* ::set(), in addition to being invoked on its own, so there are several
|
||||
* valid Actions that could have been entered.
|
||||
*/
|
||||
typedef uint32_t Action;
|
||||
enum {
|
||||
NONE = 0x00,
|
||||
GET = 0x01,
|
||||
SET = 0x02,
|
||||
CALL = 0x04,
|
||||
ENUMERATE = 0x08,
|
||||
GET_PROPERTY_DESCRIPTOR = 0x10
|
||||
};
|
||||
|
||||
virtual bool enter(JSContext* cx, HandleObject wrapper, HandleId id, Action act,
|
||||
bool* bp) const;
|
||||
|
||||
/* Standard internal methods. */
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const = 0;
|
||||
virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) const = 0;
|
||||
virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
|
||||
AutoIdVector& props) const = 0;
|
||||
virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
ObjectOpResult& result) const = 0;
|
||||
|
||||
/*
|
||||
* These methods are standard, but the engine does not normally call them.
|
||||
* They're opt-in. See "Proxy prototype chains" above.
|
||||
*
|
||||
* getPrototype() crashes if called. setPrototype() throws a TypeError.
|
||||
*/
|
||||
virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const;
|
||||
virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
|
||||
ObjectOpResult& result) const;
|
||||
|
||||
/* Non-standard but conceptual kin to {g,s}etPrototype, so these live here. */
|
||||
virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
|
||||
MutableHandleObject protop) const = 0;
|
||||
virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const;
|
||||
|
||||
virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
|
||||
ObjectOpResult& result) const = 0;
|
||||
virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const = 0;
|
||||
|
||||
/*
|
||||
* These standard internal methods are implemented, as a convenience, so
|
||||
* that ProxyHandler subclasses don't have to provide every single method.
|
||||
*
|
||||
* The base-class implementations work by calling getPropertyDescriptor().
|
||||
* They do not follow any standard. When in doubt, override them.
|
||||
*/
|
||||
virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const;
|
||||
virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp) const;
|
||||
virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result) const;
|
||||
|
||||
/*
|
||||
* [[Call]] and [[Construct]] are standard internal methods but according
|
||||
* to the spec, they are not present on every object.
|
||||
*
|
||||
* SpiderMonkey never calls a proxy's call()/construct() internal method
|
||||
* unless isCallable()/isConstructor() returns true for that proxy.
|
||||
*
|
||||
* BaseProxyHandler::isCallable()/isConstructor() always return false, and
|
||||
* BaseProxyHandler::call()/construct() crash if called. So if you're
|
||||
* creating a kind of that is never callable, you don't have to override
|
||||
* anything, but otherwise you probably want to override all four.
|
||||
*/
|
||||
virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const;
|
||||
virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const;
|
||||
|
||||
/* SpiderMonkey extensions. */
|
||||
virtual bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const;
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const;
|
||||
virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const;
|
||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
|
||||
AutoIdVector& props) const;
|
||||
virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
|
||||
const CallArgs& args) const;
|
||||
virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const;
|
||||
virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
|
||||
ESClass* cls) const;
|
||||
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
|
||||
virtual void trace(JSTracer* trc, JSObject* proxy) const;
|
||||
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
|
||||
virtual void objectMoved(JSObject* proxy, const JSObject* old) const;
|
||||
|
||||
// Allow proxies, wrappers in particular, to specify callability at runtime.
|
||||
// Note: These do not take const JSObject*, but they do in spirit.
|
||||
// We are not prepared to do this, as there's little const correctness
|
||||
// in the external APIs that handle proxies.
|
||||
virtual bool isCallable(JSObject* obj) const;
|
||||
virtual bool isConstructor(JSObject* obj) const;
|
||||
|
||||
// These two hooks must be overridden, or not overridden, in tandem -- no
|
||||
// overriding just one!
|
||||
virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
|
||||
JS::HandleObject callable) const;
|
||||
virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const;
|
||||
|
||||
virtual bool getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
|
||||
ElementAdder* adder) const;
|
||||
|
||||
/* See comment for weakmapKeyDelegateOp in js/Class.h. */
|
||||
virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const;
|
||||
virtual bool isScripted() const { return false; }
|
||||
};
|
||||
|
||||
extern JS_FRIEND_DATA(const js::Class* const) ProxyClassPtr;
|
||||
|
||||
inline bool IsProxy(const JSObject* obj)
|
||||
{
|
||||
return GetObjectClass(obj)->isProxy();
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
const uint32_t PROXY_EXTRA_SLOTS = 2;
|
||||
|
||||
// Layout of the values stored by a proxy. Note that API clients require the
|
||||
// private slot to be the first slot in the proxy's values, so that the private
|
||||
// slot can be accessed in the same fashion as the first reserved slot, via
|
||||
// {Get,Set}ReservedOrProxyPrivateSlot.
|
||||
|
||||
struct ProxyValueArray
|
||||
{
|
||||
Value privateSlot;
|
||||
Value extraSlots[PROXY_EXTRA_SLOTS];
|
||||
|
||||
ProxyValueArray()
|
||||
: privateSlot(JS::UndefinedValue())
|
||||
{
|
||||
for (size_t i = 0; i < PROXY_EXTRA_SLOTS; i++)
|
||||
extraSlots[i] = JS::UndefinedValue();
|
||||
}
|
||||
};
|
||||
|
||||
// All proxies share the same data layout. Following the object's shape and
|
||||
// type, the proxy has a ProxyDataLayout structure with a pointer to an array
|
||||
// of values and the proxy's handler. This is designed both so that proxies can
|
||||
// be easily swapped with other objects (via RemapWrapper) and to mimic the
|
||||
// layout of other objects (proxies and other objects have the same size) so
|
||||
// that common code can access either type of object.
|
||||
//
|
||||
// See GetReservedOrProxyPrivateSlot below.
|
||||
struct ProxyDataLayout
|
||||
{
|
||||
ProxyValueArray* values;
|
||||
const BaseProxyHandler* handler;
|
||||
};
|
||||
|
||||
const uint32_t ProxyDataOffset = 2 * sizeof(void*);
|
||||
|
||||
inline ProxyDataLayout*
|
||||
GetProxyDataLayout(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(IsProxy(obj));
|
||||
return reinterpret_cast<ProxyDataLayout*>(reinterpret_cast<uint8_t*>(obj) + ProxyDataOffset);
|
||||
}
|
||||
|
||||
inline const ProxyDataLayout*
|
||||
GetProxyDataLayout(const JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(IsProxy(obj));
|
||||
return reinterpret_cast<const ProxyDataLayout*>(reinterpret_cast<const uint8_t*>(obj) +
|
||||
ProxyDataOffset);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
inline const BaseProxyHandler*
|
||||
GetProxyHandler(const JSObject* obj)
|
||||
{
|
||||
return detail::GetProxyDataLayout(obj)->handler;
|
||||
}
|
||||
|
||||
inline const Value&
|
||||
GetProxyPrivate(const JSObject* obj)
|
||||
{
|
||||
return detail::GetProxyDataLayout(obj)->values->privateSlot;
|
||||
}
|
||||
|
||||
inline JSObject*
|
||||
GetProxyTargetObject(JSObject* obj)
|
||||
{
|
||||
return GetProxyPrivate(obj).toObjectOrNull();
|
||||
}
|
||||
|
||||
inline const Value&
|
||||
GetProxyExtra(const JSObject* obj, size_t n)
|
||||
{
|
||||
MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS);
|
||||
return detail::GetProxyDataLayout(obj)->values->extraSlots[n];
|
||||
}
|
||||
|
||||
inline void
|
||||
SetProxyHandler(JSObject* obj, const BaseProxyHandler* handler)
|
||||
{
|
||||
detail::GetProxyDataLayout(obj)->handler = handler;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
SetValueInProxy(Value* slot, const Value& value);
|
||||
|
||||
inline void
|
||||
SetProxyExtra(JSObject* obj, size_t n, const Value& extra)
|
||||
{
|
||||
MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS);
|
||||
Value* vp = &detail::GetProxyDataLayout(obj)->values->extraSlots[n];
|
||||
|
||||
// Trigger a barrier before writing the slot.
|
||||
if (vp->isMarkable() || extra.isMarkable())
|
||||
SetValueInProxy(vp, extra);
|
||||
else
|
||||
*vp = extra;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsScriptedProxy(const JSObject* obj)
|
||||
{
|
||||
return IsProxy(obj) && GetProxyHandler(obj)->isScripted();
|
||||
}
|
||||
|
||||
inline const Value&
|
||||
GetReservedOrProxyPrivateSlot(const JSObject* obj, size_t slot)
|
||||
{
|
||||
MOZ_ASSERT(slot == 0);
|
||||
MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)) || IsProxy(obj));
|
||||
return reinterpret_cast<const shadow::Object*>(obj)->slotRef(slot);
|
||||
}
|
||||
|
||||
inline void
|
||||
SetReservedOrProxyPrivateSlot(JSObject* obj, size_t slot, const Value& value)
|
||||
{
|
||||
MOZ_ASSERT(slot == 0);
|
||||
MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)) || IsProxy(obj));
|
||||
shadow::Object* sobj = reinterpret_cast<shadow::Object*>(obj);
|
||||
if (sobj->slotRef(slot).isMarkable() || value.isMarkable())
|
||||
SetReservedOrProxyPrivateSlotWithBarrier(obj, slot, value);
|
||||
else
|
||||
sobj->slotRef(slot) = value;
|
||||
}
|
||||
|
||||
class MOZ_STACK_CLASS ProxyOptions {
|
||||
protected:
|
||||
/* protected constructor for subclass */
|
||||
explicit ProxyOptions(bool singletonArg, bool lazyProtoArg = false)
|
||||
: singleton_(singletonArg),
|
||||
lazyProto_(lazyProtoArg),
|
||||
clasp_(ProxyClassPtr)
|
||||
{}
|
||||
|
||||
public:
|
||||
ProxyOptions() : singleton_(false),
|
||||
lazyProto_(false),
|
||||
clasp_(ProxyClassPtr)
|
||||
{}
|
||||
|
||||
bool singleton() const { return singleton_; }
|
||||
ProxyOptions& setSingleton(bool flag) {
|
||||
singleton_ = flag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool lazyProto() const { return lazyProto_; }
|
||||
ProxyOptions& setLazyProto(bool flag) {
|
||||
lazyProto_ = flag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Class* clasp() const {
|
||||
return clasp_;
|
||||
}
|
||||
ProxyOptions& setClass(const Class* claspArg) {
|
||||
clasp_ = claspArg;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
bool singleton_;
|
||||
bool lazyProto_;
|
||||
const Class* clasp_;
|
||||
};
|
||||
|
||||
JS_FRIEND_API(JSObject*)
|
||||
NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv,
|
||||
JSObject* proto, const ProxyOptions& options = ProxyOptions());
|
||||
|
||||
JSObject*
|
||||
RenewProxyObject(JSContext* cx, JSObject* obj, BaseProxyHandler* handler, const Value& priv);
|
||||
|
||||
class JS_FRIEND_API(AutoEnterPolicy)
|
||||
{
|
||||
public:
|
||||
typedef BaseProxyHandler::Action Action;
|
||||
AutoEnterPolicy(JSContext* cx, const BaseProxyHandler* handler,
|
||||
HandleObject wrapper, HandleId id, Action act, bool mayThrow)
|
||||
#ifdef JS_DEBUG
|
||||
: context(nullptr)
|
||||
#endif
|
||||
{
|
||||
allow = handler->hasSecurityPolicy() ? handler->enter(cx, wrapper, id, act, &rv)
|
||||
: true;
|
||||
recordEnter(cx, wrapper, id, act);
|
||||
// We want to throw an exception if all of the following are true:
|
||||
// * The policy disallowed access.
|
||||
// * The policy set rv to false, indicating that we should throw.
|
||||
// * The caller did not instruct us to ignore exceptions.
|
||||
// * The policy did not throw itself.
|
||||
if (!allow && !rv && mayThrow)
|
||||
reportErrorIfExceptionIsNotPending(cx, id);
|
||||
}
|
||||
|
||||
virtual ~AutoEnterPolicy() { recordLeave(); }
|
||||
inline bool allowed() { return allow; }
|
||||
inline bool returnValue() { MOZ_ASSERT(!allowed()); return rv; }
|
||||
|
||||
protected:
|
||||
// no-op constructor for subclass
|
||||
AutoEnterPolicy()
|
||||
#ifdef JS_DEBUG
|
||||
: context(nullptr)
|
||||
, enteredAction(BaseProxyHandler::NONE)
|
||||
#endif
|
||||
{}
|
||||
void reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id);
|
||||
bool allow;
|
||||
bool rv;
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
JSContext* context;
|
||||
mozilla::Maybe<HandleObject> enteredProxy;
|
||||
mozilla::Maybe<HandleId> enteredId;
|
||||
Action enteredAction;
|
||||
|
||||
// NB: We explicitly don't track the entered action here, because sometimes
|
||||
// set() methods do an implicit get() during their implementation, leading
|
||||
// to spurious assertions.
|
||||
AutoEnterPolicy* prev;
|
||||
void recordEnter(JSContext* cx, HandleObject proxy, HandleId id, Action act);
|
||||
void recordLeave();
|
||||
|
||||
friend JS_FRIEND_API(void) assertEnteredPolicy(JSContext* cx, JSObject* proxy, jsid id, Action act);
|
||||
#else
|
||||
inline void recordEnter(JSContext* cx, JSObject* proxy, jsid id, Action act) {}
|
||||
inline void recordLeave() {}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
class JS_FRIEND_API(AutoWaivePolicy) : public AutoEnterPolicy {
|
||||
public:
|
||||
AutoWaivePolicy(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
BaseProxyHandler::Action act)
|
||||
{
|
||||
allow = true;
|
||||
recordEnter(cx, proxy, id, act);
|
||||
}
|
||||
};
|
||||
#else
|
||||
class JS_FRIEND_API(AutoWaivePolicy) {
|
||||
public:
|
||||
AutoWaivePolicy(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
BaseProxyHandler::Action act)
|
||||
{}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
extern JS_FRIEND_API(void)
|
||||
assertEnteredPolicy(JSContext* cx, JSObject* obj, jsid id,
|
||||
BaseProxyHandler::Action act);
|
||||
#else
|
||||
inline void assertEnteredPolicy(JSContext* cx, JSObject* obj, jsid id,
|
||||
BaseProxyHandler::Action act)
|
||||
{}
|
||||
#endif
|
||||
|
||||
extern JS_FRIEND_API(JSObject*)
|
||||
InitProxyClass(JSContext* cx, JS::HandleObject obj);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* js_Proxy_h */
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/*
|
||||
* Ways to get various per-Realm objects. All the getters declared in this
|
||||
* header operate on the Realm corresponding to the current compartment on the
|
||||
* JSContext.
|
||||
*/
|
||||
|
||||
#ifndef js_Realm_h
|
||||
#define js_Realm_h
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
struct JSContext;
|
||||
class JSObject;
|
||||
|
||||
namespace JS {
|
||||
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
GetRealmObjectPrototype(JSContext* cx);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
GetRealmFunctionPrototype(JSContext* cx);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
GetRealmArrayPrototype(JSContext* cx);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
GetRealmErrorPrototype(JSContext* cx);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
GetRealmIteratorPrototype(JSContext* cx);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_Realm_h
|
||||
|
||||
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/*
|
||||
* Various #defines required to build SpiderMonkey. Embedders should add this
|
||||
* file to the start of the command line via -include or a similar mechanism,
|
||||
* or SpiderMonkey public headers may not work correctly.
|
||||
*/
|
||||
|
||||
#ifndef js_RequiredDefines_h
|
||||
#define js_RequiredDefines_h
|
||||
|
||||
/*
|
||||
* The c99 defining the limit macros (UINT32_MAX for example), says:
|
||||
*
|
||||
* C++ implementations should define these macros only when
|
||||
* __STDC_LIMIT_MACROS is defined before <stdint.h> is included.
|
||||
*
|
||||
* The same also occurs with __STDC_CONSTANT_MACROS for the constant macros
|
||||
* (INT8_C for example) used to specify a literal constant of the proper type,
|
||||
* and with __STDC_FORMAT_MACROS for the format macros (PRId32 for example) used
|
||||
* with the fprintf function family.
|
||||
*/
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
/* Also define a char16_t type if not provided by the compiler. */
|
||||
#include "mozilla/Char16.h"
|
||||
|
||||
#endif /* js_RequiredDefines_h */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,91 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_SliceBudget_h
|
||||
#define js_SliceBudget_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace js {
|
||||
|
||||
struct JS_PUBLIC_API(TimeBudget)
|
||||
{
|
||||
int64_t budget;
|
||||
|
||||
explicit TimeBudget(int64_t milliseconds) { budget = milliseconds; }
|
||||
};
|
||||
|
||||
struct JS_PUBLIC_API(WorkBudget)
|
||||
{
|
||||
int64_t budget;
|
||||
|
||||
explicit WorkBudget(int64_t work) { budget = work; }
|
||||
};
|
||||
|
||||
/*
|
||||
* This class records how much work has been done in a given collection slice,
|
||||
* so that we can return before pausing for too long. Some slices are allowed
|
||||
* to run for unlimited time, and others are bounded. To reduce the number of
|
||||
* gettimeofday calls, we only check the time every 1000 operations.
|
||||
*/
|
||||
class JS_PUBLIC_API(SliceBudget)
|
||||
{
|
||||
static const int64_t unlimitedDeadline = INT64_MAX;
|
||||
static const intptr_t unlimitedStartCounter = INTPTR_MAX;
|
||||
|
||||
bool checkOverBudget();
|
||||
|
||||
SliceBudget();
|
||||
|
||||
public:
|
||||
// Memory of the originally requested budget. If isUnlimited, neither of
|
||||
// these are in use. If deadline==0, then workBudget is valid. Otherwise
|
||||
// timeBudget is valid.
|
||||
TimeBudget timeBudget;
|
||||
WorkBudget workBudget;
|
||||
|
||||
int64_t deadline; /* in microseconds */
|
||||
intptr_t counter;
|
||||
|
||||
static const intptr_t CounterReset = 1000;
|
||||
|
||||
static const int64_t UnlimitedTimeBudget = -1;
|
||||
static const int64_t UnlimitedWorkBudget = -1;
|
||||
|
||||
/* Use to create an unlimited budget. */
|
||||
static SliceBudget unlimited() { return SliceBudget(); }
|
||||
|
||||
/* Instantiate as SliceBudget(TimeBudget(n)). */
|
||||
explicit SliceBudget(TimeBudget time);
|
||||
|
||||
/* Instantiate as SliceBudget(WorkBudget(n)). */
|
||||
explicit SliceBudget(WorkBudget work);
|
||||
|
||||
void makeUnlimited() {
|
||||
deadline = unlimitedDeadline;
|
||||
counter = unlimitedStartCounter;
|
||||
}
|
||||
|
||||
void step(intptr_t amt = 1) {
|
||||
counter -= amt;
|
||||
}
|
||||
|
||||
bool isOverBudget() {
|
||||
if (counter > 0)
|
||||
return false;
|
||||
return checkOverBudget();
|
||||
}
|
||||
|
||||
bool isWorkBudget() const { return deadline == 0; }
|
||||
bool isTimeBudget() const { return deadline > 0 && !isUnlimited(); }
|
||||
bool isUnlimited() const { return deadline == unlimitedDeadline; }
|
||||
|
||||
int describe(char* buffer, size_t maxlen) const;
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* js_SliceBudget_h */
|
||||
|
|
@ -1,358 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_StructuredClone_h
|
||||
#define js_StructuredClone_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BufferList.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Value.h"
|
||||
|
||||
struct JSRuntime;
|
||||
struct JSStructuredCloneReader;
|
||||
struct JSStructuredCloneWriter;
|
||||
|
||||
// API for the HTML5 internal structured cloning algorithm.
|
||||
|
||||
namespace JS {
|
||||
|
||||
enum class StructuredCloneScope : uint32_t {
|
||||
SameProcessSameThread,
|
||||
SameProcessDifferentThread,
|
||||
DifferentProcess
|
||||
};
|
||||
|
||||
enum TransferableOwnership {
|
||||
/** Transferable data has not been filled in yet */
|
||||
SCTAG_TMO_UNFILLED = 0,
|
||||
|
||||
/** Structured clone buffer does not yet own the data */
|
||||
SCTAG_TMO_UNOWNED = 1,
|
||||
|
||||
/** All values at least this large are owned by the clone buffer */
|
||||
SCTAG_TMO_FIRST_OWNED = 2,
|
||||
|
||||
/** Data is a pointer that can be freed */
|
||||
SCTAG_TMO_ALLOC_DATA = 2,
|
||||
|
||||
/** Data is a memory mapped pointer */
|
||||
SCTAG_TMO_MAPPED_DATA = 3,
|
||||
|
||||
/**
|
||||
* Data is embedding-specific. The engine can free it by calling the
|
||||
* freeTransfer op. The embedding can also use SCTAG_TMO_USER_MIN and
|
||||
* greater, up to 32 bits, to distinguish specific ownership variants.
|
||||
*/
|
||||
SCTAG_TMO_CUSTOM = 4,
|
||||
|
||||
SCTAG_TMO_USER_MIN
|
||||
};
|
||||
|
||||
class CloneDataPolicy
|
||||
{
|
||||
bool sharedArrayBuffer_;
|
||||
|
||||
public:
|
||||
// The default is to allow all policy-controlled aspects.
|
||||
|
||||
CloneDataPolicy() :
|
||||
sharedArrayBuffer_(true)
|
||||
{}
|
||||
|
||||
// In the JS engine, SharedArrayBuffers can only be cloned intra-process
|
||||
// because the shared memory areas are allocated in process-private memory.
|
||||
// Clients should therefore deny SharedArrayBuffers when cloning data that
|
||||
// are to be transmitted inter-process.
|
||||
//
|
||||
// Clients should also deny SharedArrayBuffers when cloning data that are to
|
||||
// be transmitted intra-process if policy needs dictate such denial.
|
||||
|
||||
CloneDataPolicy& denySharedArrayBuffer() {
|
||||
sharedArrayBuffer_ = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isSharedArrayBufferAllowed() const {
|
||||
return sharedArrayBuffer_;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
/**
|
||||
* Read structured data from the reader r. This hook is used to read a value
|
||||
* previously serialized by a call to the WriteStructuredCloneOp hook.
|
||||
*
|
||||
* tag and data are the pair of uint32_t values from the header. The callback
|
||||
* may use the JS_Read* APIs to read any other relevant parts of the object
|
||||
* from the reader r. closure is any value passed to the JS_ReadStructuredClone
|
||||
* function. Return the new object on success, nullptr on error/exception.
|
||||
*/
|
||||
typedef JSObject* (*ReadStructuredCloneOp)(JSContext* cx, JSStructuredCloneReader* r,
|
||||
uint32_t tag, uint32_t data, void* closure);
|
||||
|
||||
/**
|
||||
* Structured data serialization hook. The engine can write primitive values,
|
||||
* Objects, Arrays, Dates, RegExps, TypedArrays, ArrayBuffers, Sets, Maps,
|
||||
* and SharedTypedArrays. Any other type of object requires application support.
|
||||
* This callback must first use the JS_WriteUint32Pair API to write an object
|
||||
* header, passing a value greater than JS_SCTAG_USER to the tag parameter.
|
||||
* Then it can use the JS_Write* APIs to write any other relevant parts of
|
||||
* the value v to the writer w. closure is any value passed to the
|
||||
* JS_WriteStructuredClone function.
|
||||
*
|
||||
* Return true on success, false on error/exception.
|
||||
*/
|
||||
typedef bool (*WriteStructuredCloneOp)(JSContext* cx, JSStructuredCloneWriter* w,
|
||||
JS::HandleObject obj, void* closure);
|
||||
|
||||
/**
|
||||
* This is called when JS_WriteStructuredClone is given an invalid transferable.
|
||||
* To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
|
||||
* with error set to one of the JS_SCERR_* values.
|
||||
*/
|
||||
typedef void (*StructuredCloneErrorOp)(JSContext* cx, uint32_t errorid);
|
||||
|
||||
/**
|
||||
* This is called when JS_ReadStructuredClone receives a transferable object
|
||||
* not known to the engine. If this hook does not exist or returns false, the
|
||||
* JS engine calls the reportError op if set, otherwise it throws a
|
||||
* DATA_CLONE_ERR DOM Exception. This method is called before any other
|
||||
* callback and must return a non-null object in returnObject on success.
|
||||
*/
|
||||
typedef bool (*ReadTransferStructuredCloneOp)(JSContext* cx, JSStructuredCloneReader* r,
|
||||
uint32_t tag, void* content, uint64_t extraData,
|
||||
void* closure,
|
||||
JS::MutableHandleObject returnObject);
|
||||
|
||||
/**
|
||||
* Called when JS_WriteStructuredClone receives a transferable object not
|
||||
* handled by the engine. If this hook does not exist or returns false, the JS
|
||||
* engine will call the reportError hook or fall back to throwing a
|
||||
* DATA_CLONE_ERR DOM Exception. This method is called before any other
|
||||
* callback.
|
||||
*
|
||||
* tag: indicates what type of transferable this is. Must be greater than
|
||||
* 0xFFFF0201 (value of the internal SCTAG_TRANSFER_MAP_PENDING_ENTRY)
|
||||
*
|
||||
* ownership: see TransferableOwnership, above. Used to communicate any needed
|
||||
* ownership info to the FreeTransferStructuredCloneOp.
|
||||
*
|
||||
* content, extraData: what the ReadTransferStructuredCloneOp will receive
|
||||
*/
|
||||
typedef bool (*TransferStructuredCloneOp)(JSContext* cx,
|
||||
JS::Handle<JSObject*> obj,
|
||||
void* closure,
|
||||
// Output:
|
||||
uint32_t* tag,
|
||||
JS::TransferableOwnership* ownership,
|
||||
void** content,
|
||||
uint64_t* extraData);
|
||||
|
||||
/**
|
||||
* Called when freeing an unknown transferable object. Note that it
|
||||
* should never trigger a garbage collection (and will assert in a
|
||||
* debug build if it does.)
|
||||
*/
|
||||
typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership,
|
||||
void* content, uint64_t extraData, void* closure);
|
||||
|
||||
// The maximum supported structured-clone serialization format version.
|
||||
// Increment this when anything at all changes in the serialization format.
|
||||
// (Note that this does not need to be bumped for Transferable-only changes,
|
||||
// since they are never saved to persistent storage.)
|
||||
#define JS_STRUCTURED_CLONE_VERSION 8
|
||||
|
||||
struct JSStructuredCloneCallbacks {
|
||||
ReadStructuredCloneOp read;
|
||||
WriteStructuredCloneOp write;
|
||||
StructuredCloneErrorOp reportError;
|
||||
ReadTransferStructuredCloneOp readTransfer;
|
||||
TransferStructuredCloneOp writeTransfer;
|
||||
FreeTransferStructuredCloneOp freeTransfer;
|
||||
};
|
||||
|
||||
enum OwnTransferablePolicy {
|
||||
OwnsTransferablesIfAny,
|
||||
IgnoreTransferablesIfAny,
|
||||
NoTransferables
|
||||
};
|
||||
|
||||
class MOZ_NON_MEMMOVABLE JSStructuredCloneData : public mozilla::BufferList<js::SystemAllocPolicy>
|
||||
{
|
||||
typedef js::SystemAllocPolicy AllocPolicy;
|
||||
typedef mozilla::BufferList<js::SystemAllocPolicy> BufferList;
|
||||
|
||||
static const size_t kInitialSize = 0;
|
||||
static const size_t kInitialCapacity = 4096;
|
||||
static const size_t kStandardCapacity = 4096;
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks_;
|
||||
void* closure_;
|
||||
OwnTransferablePolicy ownTransferables_;
|
||||
|
||||
void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks,
|
||||
void* closure,
|
||||
OwnTransferablePolicy policy) {
|
||||
callbacks_ = callbacks;
|
||||
closure_ = closure;
|
||||
ownTransferables_ = policy;
|
||||
}
|
||||
|
||||
friend struct JSStructuredCloneWriter;
|
||||
friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer);
|
||||
|
||||
public:
|
||||
explicit JSStructuredCloneData(AllocPolicy aAP = AllocPolicy())
|
||||
: BufferList(kInitialSize, kInitialCapacity, kStandardCapacity, aAP)
|
||||
, callbacks_(nullptr)
|
||||
, closure_(nullptr)
|
||||
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
|
||||
{}
|
||||
MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers)
|
||||
: BufferList(Move(buffers))
|
||||
, callbacks_(nullptr)
|
||||
, closure_(nullptr)
|
||||
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
|
||||
{}
|
||||
JSStructuredCloneData(JSStructuredCloneData&& other) = default;
|
||||
JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default;
|
||||
~JSStructuredCloneData();
|
||||
|
||||
using BufferList::BufferList;
|
||||
};
|
||||
|
||||
/** Note: if the *data contains transferable objects, it can be read only once. */
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, uint32_t version,
|
||||
JS::StructuredCloneScope scope,
|
||||
JS::MutableHandleValue vp,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, JSStructuredCloneData* data,
|
||||
JS::StructuredCloneScope scope,
|
||||
JS::CloneDataPolicy cloneDataPolicy,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void* closure, JS::HandleValue transferable);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_StructuredCloneHasTransferables(JSStructuredCloneData& data, bool* hasTransferable);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
|
||||
|
||||
/** RAII sugar for JS_WriteStructuredClone. */
|
||||
class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
|
||||
const JS::StructuredCloneScope scope_;
|
||||
JSStructuredCloneData data_;
|
||||
uint32_t version_;
|
||||
|
||||
public:
|
||||
JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
|
||||
const JSStructuredCloneCallbacks* callbacks, void* closure)
|
||||
: scope_(scope), version_(JS_STRUCTURED_CLONE_VERSION)
|
||||
{
|
||||
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
|
||||
}
|
||||
|
||||
JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
|
||||
JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other);
|
||||
|
||||
~JSAutoStructuredCloneBuffer() { clear(); }
|
||||
|
||||
JSStructuredCloneData& data() { return data_; }
|
||||
bool empty() const { return !data_.Size(); }
|
||||
|
||||
void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
/** Copy some memory. It will be automatically freed by the destructor. */
|
||||
bool copy(const JSStructuredCloneData& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
|
||||
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
/**
|
||||
* Adopt some memory. It will be automatically freed by the destructor.
|
||||
* data must have been allocated by the JS engine (e.g., extracted via
|
||||
* JSAutoStructuredCloneBuffer::steal).
|
||||
*/
|
||||
void adopt(JSStructuredCloneData&& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
|
||||
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
/**
|
||||
* Release the buffer and transfer ownership to the caller.
|
||||
*/
|
||||
void steal(JSStructuredCloneData* data, uint32_t* versionp=nullptr,
|
||||
const JSStructuredCloneCallbacks** callbacks=nullptr, void** closure=nullptr);
|
||||
|
||||
/**
|
||||
* Abandon ownership of any transferable objects stored in the buffer,
|
||||
* without freeing the buffer itself. Useful when copying the data out into
|
||||
* an external container, though note that you will need to use adopt() to
|
||||
* properly release that data eventually.
|
||||
*/
|
||||
void abandon() { data_.ownTransferables_ = OwnTransferablePolicy::IgnoreTransferablesIfAny; }
|
||||
|
||||
bool read(JSContext* cx, JS::MutableHandleValue vp,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
bool write(JSContext* cx, JS::HandleValue v,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable,
|
||||
JS::CloneDataPolicy cloneDataPolicy,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
private:
|
||||
// Copy and assignment are not supported.
|
||||
JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& other) = delete;
|
||||
JSAutoStructuredCloneBuffer& operator=(const JSAutoStructuredCloneBuffer& other) = delete;
|
||||
};
|
||||
|
||||
// The range of tag values the application may use for its own custom object types.
|
||||
#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000)
|
||||
#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF)
|
||||
|
||||
#define JS_SCERR_RECURSION 0
|
||||
#define JS_SCERR_TRANSFERABLE 1
|
||||
#define JS_SCERR_DUP_TRANSFERABLE 2
|
||||
#define JS_SCERR_UNSUPPORTED_TYPE 3
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1, uint32_t* p2);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ReadBytes(JSStructuredCloneReader* r, void* p, size_t len);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ReadTypedArray(JSStructuredCloneReader* r, JS::MutableHandleValue vp);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_WriteUint32Pair(JSStructuredCloneWriter* w, uint32_t tag, uint32_t data);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_WriteBytes(JSStructuredCloneWriter* w, const void* p, size_t len);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_WriteString(JSStructuredCloneWriter* w, JS::HandleString str);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_WriteTypedArray(JSStructuredCloneWriter* w, JS::HandleValue v);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ObjectNotWritten(JSStructuredCloneWriter* w, JS::HandleObject obj);
|
||||
|
||||
JS_PUBLIC_API(JS::StructuredCloneScope)
|
||||
JS_GetStructuredCloneScope(JSStructuredCloneWriter* w);
|
||||
|
||||
#endif /* js_StructuredClone_h */
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_SweepingAPI_h
|
||||
#define js_SweepingAPI_h
|
||||
|
||||
#include "js/HeapAPI.h"
|
||||
|
||||
namespace js {
|
||||
template <typename T>
|
||||
class WeakCacheBase {};
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
template <typename T> class WeakCache;
|
||||
|
||||
namespace shadow {
|
||||
JS_PUBLIC_API(void)
|
||||
RegisterWeakCache(JS::Zone* zone, JS::WeakCache<void*>* cachep);
|
||||
} // namespace shadow
|
||||
|
||||
// A WeakCache stores the given Sweepable container and links itself into a
|
||||
// list of such caches that are swept during each GC.
|
||||
template <typename T>
|
||||
class WeakCache : public js::WeakCacheBase<T>,
|
||||
private mozilla::LinkedListElement<WeakCache<T>>
|
||||
{
|
||||
friend class mozilla::LinkedListElement<WeakCache<T>>;
|
||||
friend class mozilla::LinkedList<WeakCache<T>>;
|
||||
|
||||
WeakCache() = delete;
|
||||
WeakCache(const WeakCache&) = delete;
|
||||
|
||||
using SweepFn = void (*)(T*);
|
||||
SweepFn sweeper;
|
||||
T cache;
|
||||
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
template <typename U>
|
||||
WeakCache(Zone* zone, U&& initial)
|
||||
: cache(mozilla::Forward<U>(initial))
|
||||
{
|
||||
sweeper = GCPolicy<T>::sweep;
|
||||
shadow::RegisterWeakCache(zone, reinterpret_cast<WeakCache<void*>*>(this));
|
||||
}
|
||||
WeakCache(WeakCache&& other)
|
||||
: sweeper(other.sweeper),
|
||||
cache(mozilla::Move(other.cache))
|
||||
{
|
||||
}
|
||||
|
||||
const T& get() const { return cache; }
|
||||
T& get() { return cache; }
|
||||
|
||||
void sweep() { sweeper(&cache); }
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_SweepingAPI_h
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_TraceKind_h
|
||||
#define js_TraceKind_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
// Forward declarations of all the types a TraceKind can denote.
|
||||
namespace js {
|
||||
class BaseShape;
|
||||
class LazyScript;
|
||||
class ObjectGroup;
|
||||
class Shape;
|
||||
class Scope;
|
||||
namespace jit {
|
||||
class JitCode;
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
// When tracing a thing, the GC needs to know about the layout of the object it
|
||||
// is looking at. There are a fixed number of different layouts that the GC
|
||||
// knows about. The "trace kind" is a static map which tells which layout a GC
|
||||
// thing has.
|
||||
//
|
||||
// Although this map is public, the details are completely hidden. Not all of
|
||||
// the matching C++ types are exposed, and those that are, are opaque.
|
||||
//
|
||||
// See Value::gcKind() and JSTraceCallback in Tracer.h for more details.
|
||||
enum class TraceKind
|
||||
{
|
||||
// These trace kinds have a publicly exposed, although opaque, C++ type.
|
||||
// Note: The order here is determined by our Value packing. Other users
|
||||
// should sort alphabetically, for consistency.
|
||||
Object = 0x00,
|
||||
String = 0x01,
|
||||
Symbol = 0x02,
|
||||
Script = 0x03,
|
||||
|
||||
// Shape details are exposed through JS_TraceShapeCycleCollectorChildren.
|
||||
Shape = 0x04,
|
||||
|
||||
// ObjectGroup details are exposed through JS_TraceObjectGroupCycleCollectorChildren.
|
||||
ObjectGroup = 0x05,
|
||||
|
||||
// The kind associated with a nullptr.
|
||||
Null = 0x06,
|
||||
|
||||
// The following kinds do not have an exposed C++ idiom.
|
||||
BaseShape = 0x0F,
|
||||
JitCode = 0x1F,
|
||||
LazyScript = 0x2F,
|
||||
Scope = 0x3F
|
||||
};
|
||||
const static uintptr_t OutOfLineTraceKindMask = 0x07;
|
||||
static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
static_assert(uintptr_t(JS::TraceKind::Scope) & OutOfLineTraceKindMask, "mask bits are set");
|
||||
|
||||
// When this header is imported inside SpiderMonkey, the class definitions are
|
||||
// available and we can query those definitions to find the correct kind
|
||||
// directly from the class hierarchy.
|
||||
template <typename T>
|
||||
struct MapTypeToTraceKind {
|
||||
static const JS::TraceKind kind = T::TraceKind;
|
||||
};
|
||||
|
||||
// When this header is used outside SpiderMonkey, the class definitions are not
|
||||
// available, so the following table containing all public GC types is used.
|
||||
#define JS_FOR_EACH_TRACEKIND(D) \
|
||||
/* PrettyName TypeName AddToCCKind */ \
|
||||
D(BaseShape, js::BaseShape, true) \
|
||||
D(JitCode, js::jit::JitCode, true) \
|
||||
D(LazyScript, js::LazyScript, true) \
|
||||
D(Scope, js::Scope, true) \
|
||||
D(Object, JSObject, true) \
|
||||
D(ObjectGroup, js::ObjectGroup, true) \
|
||||
D(Script, JSScript, true) \
|
||||
D(Shape, js::Shape, true) \
|
||||
D(String, JSString, false) \
|
||||
D(Symbol, JS::Symbol, false)
|
||||
|
||||
// Map from all public types to their trace kind.
|
||||
#define JS_EXPAND_DEF(name, type, _) \
|
||||
template <> struct MapTypeToTraceKind<type> { \
|
||||
static const JS::TraceKind kind = JS::TraceKind::name; \
|
||||
};
|
||||
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
|
||||
#undef JS_EXPAND_DEF
|
||||
|
||||
// RootKind is closely related to TraceKind. Whereas TraceKind's indices are
|
||||
// laid out for convenient embedding as a pointer tag, the indicies of RootKind
|
||||
// are designed for use as array keys via EnumeratedArray.
|
||||
enum class RootKind : int8_t
|
||||
{
|
||||
// These map 1:1 with trace kinds.
|
||||
#define EXPAND_ROOT_KIND(name, _0, _1) \
|
||||
name,
|
||||
JS_FOR_EACH_TRACEKIND(EXPAND_ROOT_KIND)
|
||||
#undef EXPAND_ROOT_KIND
|
||||
|
||||
// These tagged pointers are special-cased for performance.
|
||||
Id,
|
||||
Value,
|
||||
|
||||
// Everything else.
|
||||
Traceable,
|
||||
|
||||
Limit
|
||||
};
|
||||
|
||||
// Most RootKind correspond directly to a trace kind.
|
||||
template <TraceKind traceKind> struct MapTraceKindToRootKind {};
|
||||
#define JS_EXPAND_DEF(name, _0, _1) \
|
||||
template <> struct MapTraceKindToRootKind<JS::TraceKind::name> { \
|
||||
static const JS::RootKind kind = JS::RootKind::name; \
|
||||
};
|
||||
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF)
|
||||
#undef JS_EXPAND_DEF
|
||||
|
||||
// Specify the RootKind for all types. Value and jsid map to special cases;
|
||||
// pointer types we can derive directly from the TraceKind; everything else
|
||||
// should go in the Traceable list and use GCPolicy<T>::trace for tracing.
|
||||
template <typename T>
|
||||
struct MapTypeToRootKind {
|
||||
static const JS::RootKind kind = JS::RootKind::Traceable;
|
||||
};
|
||||
template <typename T>
|
||||
struct MapTypeToRootKind<T*> {
|
||||
static const JS::RootKind kind =
|
||||
JS::MapTraceKindToRootKind<JS::MapTypeToTraceKind<T>::kind>::kind;
|
||||
};
|
||||
template <typename T>
|
||||
struct MapTypeToRootKind<mozilla::UniquePtr<T>> {
|
||||
static const JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
|
||||
};
|
||||
template <> struct MapTypeToRootKind<JS::Value> {
|
||||
static const JS::RootKind kind = JS::RootKind::Value;
|
||||
};
|
||||
template <> struct MapTypeToRootKind<jsid> {
|
||||
static const JS::RootKind kind = JS::RootKind::Id;
|
||||
};
|
||||
template <> struct MapTypeToRootKind<JSFunction*> : public MapTypeToRootKind<JSObject*> {};
|
||||
|
||||
// Fortunately, few places in the system need to deal with fully abstract
|
||||
// cells. In those places that do, we generally want to move to a layout
|
||||
// templated function as soon as possible. This template wraps the upcast
|
||||
// for that dispatch.
|
||||
//
|
||||
// Given a call:
|
||||
//
|
||||
// DispatchTraceKindTyped(f, thing, traceKind, ... args)
|
||||
//
|
||||
// Downcast the |void *thing| to the specific type designated by |traceKind|,
|
||||
// and pass it to the functor |f| along with |... args|, forwarded. Pass the
|
||||
// type designated by |traceKind| as the functor's template argument. The
|
||||
// |thing| parameter is optional; without it, we simply pass through |... args|.
|
||||
|
||||
// GCC and Clang require an explicit template declaration in front of the
|
||||
// specialization of operator() because it is a dependent template. MSVC, on
|
||||
// the other hand, gets very confused if we have a |template| token there.
|
||||
// The clang-cl front end defines _MSC_VER, but still requires the explicit
|
||||
// template declaration, so we must test for __clang__ here as well.
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define JS_DEPENDENT_TEMPLATE_HINT
|
||||
#else
|
||||
# define JS_DEPENDENT_TEMPLATE_HINT template
|
||||
#endif
|
||||
template <typename F, typename... Args>
|
||||
auto
|
||||
DispatchTraceKindTyped(F f, JS::TraceKind traceKind, Args&&... args)
|
||||
-> decltype(f. JS_DEPENDENT_TEMPLATE_HINT operator()<JSObject>(mozilla::Forward<Args>(args)...))
|
||||
{
|
||||
switch (traceKind) {
|
||||
#define JS_EXPAND_DEF(name, type, _) \
|
||||
case JS::TraceKind::name: \
|
||||
return f. JS_DEPENDENT_TEMPLATE_HINT operator()<type>(mozilla::Forward<Args>(args)...);
|
||||
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
|
||||
#undef JS_EXPAND_DEF
|
||||
default:
|
||||
MOZ_CRASH("Invalid trace kind in DispatchTraceKindTyped.");
|
||||
}
|
||||
}
|
||||
#undef JS_DEPENDENT_TEMPLATE_HINT
|
||||
|
||||
template <typename F, typename... Args>
|
||||
auto
|
||||
DispatchTraceKindTyped(F f, void* thing, JS::TraceKind traceKind, Args&&... args)
|
||||
-> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
|
||||
{
|
||||
switch (traceKind) {
|
||||
#define JS_EXPAND_DEF(name, type, _) \
|
||||
case JS::TraceKind::name: \
|
||||
return f(static_cast<type*>(thing), mozilla::Forward<Args>(args)...);
|
||||
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
|
||||
#undef JS_EXPAND_DEF
|
||||
default:
|
||||
MOZ_CRASH("Invalid trace kind in DispatchTraceKindTyped.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_TraceKind_h
|
||||
|
|
@ -1,403 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_TracingAPI_h
|
||||
#define js_TracingAPI_h
|
||||
|
||||
#include "jsalloc.h"
|
||||
|
||||
#include "js/HashTable.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/TraceKind.h"
|
||||
|
||||
class JS_PUBLIC_API(JSTracer);
|
||||
|
||||
namespace JS {
|
||||
class JS_PUBLIC_API(CallbackTracer);
|
||||
template <typename T> class Heap;
|
||||
template <typename T> class TenuredHeap;
|
||||
|
||||
/** Returns a static string equivalent of |kind|. */
|
||||
JS_FRIEND_API(const char*)
|
||||
GCTraceKindToAscii(JS::TraceKind kind);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
enum WeakMapTraceKind {
|
||||
/**
|
||||
* Do not trace into weak map keys or values during traversal. Users must
|
||||
* handle weak maps manually.
|
||||
*/
|
||||
DoNotTraceWeakMaps,
|
||||
|
||||
/**
|
||||
* Do true ephemeron marking with a weak key lookup marking phase. This is
|
||||
* the default for GCMarker.
|
||||
*/
|
||||
ExpandWeakMaps,
|
||||
|
||||
/**
|
||||
* Trace through to all values, irrespective of whether the keys are live
|
||||
* or not. Used for non-marking tracers.
|
||||
*/
|
||||
TraceWeakMapValues,
|
||||
|
||||
/**
|
||||
* Trace through to all keys and values, irrespective of whether the keys
|
||||
* are live or not. Used for non-marking tracers.
|
||||
*/
|
||||
TraceWeakMapKeysValues
|
||||
};
|
||||
|
||||
class JS_PUBLIC_API(JSTracer)
|
||||
{
|
||||
public:
|
||||
// Return the runtime set on the tracer.
|
||||
JSRuntime* runtime() const { return runtime_; }
|
||||
|
||||
// Return the weak map tracing behavior currently set on this tracer.
|
||||
WeakMapTraceKind weakMapAction() const { return weakMapAction_; }
|
||||
|
||||
enum class TracerKindTag {
|
||||
// Marking path: a tracer used only for marking liveness of cells, not
|
||||
// for moving them. The kind will transition to WeakMarking after
|
||||
// everything reachable by regular edges has been marked.
|
||||
Marking,
|
||||
|
||||
// Same as Marking, except we have now moved on to the "weak marking
|
||||
// phase", in which every marked obj/script is immediately looked up to
|
||||
// see if it is a weak map key (and therefore might require marking its
|
||||
// weak map value).
|
||||
WeakMarking,
|
||||
|
||||
// A tracer that traverses the graph for the purposes of moving objects
|
||||
// from the nursery to the tenured area.
|
||||
Tenuring,
|
||||
|
||||
// General-purpose traversal that invokes a callback on each cell.
|
||||
// Traversing children is the responsibility of the callback.
|
||||
Callback
|
||||
};
|
||||
bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; }
|
||||
bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; }
|
||||
bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
|
||||
bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
|
||||
inline JS::CallbackTracer* asCallbackTracer();
|
||||
#ifdef DEBUG
|
||||
bool checkEdges() { return checkEdges_; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
JSTracer(JSRuntime* rt, TracerKindTag tag,
|
||||
WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
|
||||
: runtime_(rt)
|
||||
, weakMapAction_(weakTraceKind)
|
||||
#ifdef DEBUG
|
||||
, checkEdges_(true)
|
||||
#endif
|
||||
, tag_(tag)
|
||||
{}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Set whether to check edges are valid in debug builds.
|
||||
void setCheckEdges(bool check) {
|
||||
checkEdges_ = check;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
JSRuntime* runtime_;
|
||||
WeakMapTraceKind weakMapAction_;
|
||||
#ifdef DEBUG
|
||||
bool checkEdges_;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
TracerKindTag tag_;
|
||||
};
|
||||
|
||||
namespace JS {
|
||||
|
||||
class AutoTracingName;
|
||||
class AutoTracingIndex;
|
||||
class AutoTracingCallback;
|
||||
|
||||
class JS_PUBLIC_API(CallbackTracer) : public JSTracer
|
||||
{
|
||||
public:
|
||||
CallbackTracer(JSRuntime* rt, WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
|
||||
: JSTracer(rt, JSTracer::TracerKindTag::Callback, weakTraceKind),
|
||||
contextName_(nullptr), contextIndex_(InvalidIndex), contextFunctor_(nullptr)
|
||||
{}
|
||||
CallbackTracer(JSContext* cx, WeakMapTraceKind weakTraceKind = TraceWeakMapValues);
|
||||
|
||||
// Override these methods to receive notification when an edge is visited
|
||||
// with the type contained in the callback. The default implementation
|
||||
// dispatches to the fully-generic onChild implementation, so for cases that
|
||||
// do not care about boxing overhead and do not need the actual edges,
|
||||
// just override the generic onChild.
|
||||
virtual void onObjectEdge(JSObject** objp) { onChild(JS::GCCellPtr(*objp)); }
|
||||
virtual void onStringEdge(JSString** strp) { onChild(JS::GCCellPtr(*strp)); }
|
||||
virtual void onSymbolEdge(JS::Symbol** symp) { onChild(JS::GCCellPtr(*symp)); }
|
||||
virtual void onScriptEdge(JSScript** scriptp) { onChild(JS::GCCellPtr(*scriptp)); }
|
||||
virtual void onShapeEdge(js::Shape** shapep) {
|
||||
onChild(JS::GCCellPtr(*shapep, JS::TraceKind::Shape));
|
||||
}
|
||||
virtual void onObjectGroupEdge(js::ObjectGroup** groupp) {
|
||||
onChild(JS::GCCellPtr(*groupp, JS::TraceKind::ObjectGroup));
|
||||
}
|
||||
virtual void onBaseShapeEdge(js::BaseShape** basep) {
|
||||
onChild(JS::GCCellPtr(*basep, JS::TraceKind::BaseShape));
|
||||
}
|
||||
virtual void onJitCodeEdge(js::jit::JitCode** codep) {
|
||||
onChild(JS::GCCellPtr(*codep, JS::TraceKind::JitCode));
|
||||
}
|
||||
virtual void onLazyScriptEdge(js::LazyScript** lazyp) {
|
||||
onChild(JS::GCCellPtr(*lazyp, JS::TraceKind::LazyScript));
|
||||
}
|
||||
virtual void onScopeEdge(js::Scope** scopep) {
|
||||
onChild(JS::GCCellPtr(*scopep, JS::TraceKind::Scope));
|
||||
}
|
||||
|
||||
// Override this method to receive notification when a node in the GC
|
||||
// heap graph is visited.
|
||||
virtual void onChild(const JS::GCCellPtr& thing) = 0;
|
||||
|
||||
// Access to the tracing context:
|
||||
// When tracing with a JS::CallbackTracer, we invoke the callback with the
|
||||
// edge location and the type of target. This is useful for operating on
|
||||
// the edge in the abstract or on the target thing, satisfying most common
|
||||
// use cases. However, some tracers need additional detail about the
|
||||
// specific edge that is being traced in order to be useful. Unfortunately,
|
||||
// the raw pointer to the edge that we provide is not enough information to
|
||||
// infer much of anything useful about that edge.
|
||||
//
|
||||
// In order to better support use cases that care in particular about edges
|
||||
// -- as opposed to the target thing -- tracing implementations are
|
||||
// responsible for providing extra context information about each edge they
|
||||
// trace, as it is traced. This contains, at a minimum, an edge name and,
|
||||
// when tracing an array, the index. Further specialization can be achived
|
||||
// (with some complexity), by associating a functor with the tracer so
|
||||
// that, when requested, the user can generate totally custom edge
|
||||
// descriptions.
|
||||
|
||||
// Returns the current edge's name. It is only valid to call this when
|
||||
// inside the trace callback, however, the edge name will always be set.
|
||||
const char* contextName() const { MOZ_ASSERT(contextName_); return contextName_; }
|
||||
|
||||
// Returns the current edge's index, if marked as part of an array of edges.
|
||||
// This must be called only inside the trace callback. When not tracing an
|
||||
// array, the value will be InvalidIndex.
|
||||
const static size_t InvalidIndex = size_t(-1);
|
||||
size_t contextIndex() const { return contextIndex_; }
|
||||
|
||||
// Build a description of this edge in the heap graph. This call may invoke
|
||||
// the context functor, if set, which may inspect arbitrary areas of the
|
||||
// heap. On the other hand, the description provided by this method may be
|
||||
// substantially more accurate and useful than those provided by only the
|
||||
// contextName and contextIndex.
|
||||
void getTracingEdgeName(char* buffer, size_t bufferSize);
|
||||
|
||||
// The trace implementation may associate a callback with one or more edges
|
||||
// using AutoTracingDetails. This functor is called by getTracingEdgeName
|
||||
// and is responsible for providing a textual representation of the
|
||||
// currently being traced edge. The callback has access to the full heap,
|
||||
// including the currently set tracing context.
|
||||
class ContextFunctor {
|
||||
public:
|
||||
virtual void operator()(CallbackTracer* trc, char* buf, size_t bufsize) = 0;
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
enum class TracerKind { DoNotCare, Moving, GrayBuffering, VerifyTraceProtoAndIface };
|
||||
virtual TracerKind getTracerKind() const { return TracerKind::DoNotCare; }
|
||||
#endif
|
||||
|
||||
// In C++, overriding a method hides all methods in the base class with
|
||||
// that name, not just methods with that signature. Thus, the typed edge
|
||||
// methods have to have distinct names to allow us to override them
|
||||
// individually, which is freqently useful if, for example, we only want to
|
||||
// process only one type of edge.
|
||||
void dispatchToOnEdge(JSObject** objp) { onObjectEdge(objp); }
|
||||
void dispatchToOnEdge(JSString** strp) { onStringEdge(strp); }
|
||||
void dispatchToOnEdge(JS::Symbol** symp) { onSymbolEdge(symp); }
|
||||
void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); }
|
||||
void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); }
|
||||
void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); }
|
||||
void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); }
|
||||
void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); }
|
||||
void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); }
|
||||
void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); }
|
||||
|
||||
private:
|
||||
friend class AutoTracingName;
|
||||
const char* contextName_;
|
||||
|
||||
friend class AutoTracingIndex;
|
||||
size_t contextIndex_;
|
||||
|
||||
friend class AutoTracingDetails;
|
||||
ContextFunctor* contextFunctor_;
|
||||
};
|
||||
|
||||
// Set the name portion of the tracer's context for the current edge.
|
||||
class MOZ_RAII AutoTracingName
|
||||
{
|
||||
CallbackTracer* trc_;
|
||||
const char* prior_;
|
||||
|
||||
public:
|
||||
AutoTracingName(CallbackTracer* trc, const char* name) : trc_(trc), prior_(trc->contextName_) {
|
||||
MOZ_ASSERT(name);
|
||||
trc->contextName_ = name;
|
||||
}
|
||||
~AutoTracingName() {
|
||||
MOZ_ASSERT(trc_->contextName_);
|
||||
trc_->contextName_ = prior_;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the index portion of the tracer's context for the current range.
|
||||
class MOZ_RAII AutoTracingIndex
|
||||
{
|
||||
CallbackTracer* trc_;
|
||||
|
||||
public:
|
||||
explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(nullptr) {
|
||||
if (trc->isCallbackTracer()) {
|
||||
trc_ = trc->asCallbackTracer();
|
||||
MOZ_ASSERT(trc_->contextIndex_ == CallbackTracer::InvalidIndex);
|
||||
trc_->contextIndex_ = initial;
|
||||
}
|
||||
}
|
||||
~AutoTracingIndex() {
|
||||
if (trc_) {
|
||||
MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex);
|
||||
trc_->contextIndex_ = CallbackTracer::InvalidIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void operator++() {
|
||||
if (trc_) {
|
||||
MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex);
|
||||
++trc_->contextIndex_;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Set a context callback for the trace callback to use, if it needs a detailed
|
||||
// edge description.
|
||||
class MOZ_RAII AutoTracingDetails
|
||||
{
|
||||
CallbackTracer* trc_;
|
||||
|
||||
public:
|
||||
AutoTracingDetails(JSTracer* trc, CallbackTracer::ContextFunctor& func) : trc_(nullptr) {
|
||||
if (trc->isCallbackTracer()) {
|
||||
trc_ = trc->asCallbackTracer();
|
||||
MOZ_ASSERT(trc_->contextFunctor_ == nullptr);
|
||||
trc_->contextFunctor_ = &func;
|
||||
}
|
||||
}
|
||||
~AutoTracingDetails() {
|
||||
if (trc_) {
|
||||
MOZ_ASSERT(trc_->contextFunctor_);
|
||||
trc_->contextFunctor_ = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
JS::CallbackTracer*
|
||||
JSTracer::asCallbackTracer()
|
||||
{
|
||||
MOZ_ASSERT(isCallbackTracer());
|
||||
return static_cast<JS::CallbackTracer*>(this);
|
||||
}
|
||||
|
||||
namespace JS {
|
||||
|
||||
// The JS::TraceEdge family of functions traces the given GC thing reference.
|
||||
// This performs the tracing action configured on the given JSTracer: typically
|
||||
// calling the JSTracer::callback or marking the thing as live.
|
||||
//
|
||||
// The argument to JS::TraceEdge is an in-out param: when the function returns,
|
||||
// the garbage collector might have moved the GC thing. In this case, the
|
||||
// reference passed to JS::TraceEdge will be updated to the thing's new
|
||||
// location. Callers of this method are responsible for updating any state that
|
||||
// is dependent on the object's address. For example, if the object's address
|
||||
// is used as a key in a hashtable, then the object must be removed and
|
||||
// re-inserted with the correct hash.
|
||||
//
|
||||
// Note that while |edgep| must never be null, it is fine for |*edgep| to be
|
||||
// nullptr.
|
||||
template <typename T>
|
||||
extern JS_PUBLIC_API(void)
|
||||
TraceEdge(JSTracer* trc, JS::Heap<T>* edgep, const char* name);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
TraceEdge(JSTracer* trc, JS::TenuredHeap<JSObject*>* edgep, const char* name);
|
||||
|
||||
// Edges that are always traced as part of root marking do not require
|
||||
// incremental barriers. This function allows for marking non-barriered
|
||||
// pointers, but asserts that this happens during root marking.
|
||||
//
|
||||
// Note that while |edgep| must never be null, it is fine for |*edgep| to be
|
||||
// nullptr.
|
||||
template <typename T>
|
||||
extern JS_PUBLIC_API(void)
|
||||
UnsafeTraceRoot(JSTracer* trc, T* edgep, const char* name);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
TraceChildren(JSTracer* trc, GCCellPtr thing);
|
||||
|
||||
using ZoneSet = js::HashSet<Zone*, js::DefaultHasher<Zone*>, js::SystemAllocPolicy>;
|
||||
using CompartmentSet = js::HashSet<JSCompartment*, js::DefaultHasher<JSCompartment*>,
|
||||
js::SystemAllocPolicy>;
|
||||
|
||||
/**
|
||||
* Trace every value within |compartments| that is wrapped by a
|
||||
* cross-compartment wrapper from a compartment that is not an element of
|
||||
* |compartments|.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc,
|
||||
void* thing, JS::TraceKind kind, bool includeDetails);
|
||||
|
||||
namespace js {
|
||||
|
||||
// Trace an edge that is not a GC root and is not wrapped in a barriered
|
||||
// wrapper for some reason.
|
||||
//
|
||||
// This method does not check if |*edgep| is non-null before tracing through
|
||||
// it, so callers must check any nullable pointer before calling this method.
|
||||
template <typename T>
|
||||
extern JS_PUBLIC_API(void)
|
||||
UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* edgep, const char* name);
|
||||
|
||||
namespace gc {
|
||||
|
||||
// Return true if the given edge is not live and is about to be swept.
|
||||
template <typename T>
|
||||
extern JS_PUBLIC_API(bool)
|
||||
EdgeNeedsSweep(JS::Heap<T>* edgep);
|
||||
|
||||
// Not part of the public API, but declared here so we can use it in GCPolicy
|
||||
// which is.
|
||||
template <typename T>
|
||||
bool
|
||||
IsAboutToBeFinalizedUnbarriered(T* thingp);
|
||||
|
||||
} // namespace gc
|
||||
} // namespace js
|
||||
|
||||
#endif /* js_TracingAPI_h */
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_TrackedOptimizationInfo_h
|
||||
#define js_TrackedOptimizationInfo_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
#define TRACKED_STRATEGY_LIST(_) \
|
||||
_(GetProp_ArgumentsLength) \
|
||||
_(GetProp_ArgumentsCallee) \
|
||||
_(GetProp_InferredConstant) \
|
||||
_(GetProp_Constant) \
|
||||
_(GetProp_NotDefined) \
|
||||
_(GetProp_StaticName) \
|
||||
_(GetProp_SimdGetter) \
|
||||
_(GetProp_TypedObject) \
|
||||
_(GetProp_DefiniteSlot) \
|
||||
_(GetProp_Unboxed) \
|
||||
_(GetProp_CommonGetter) \
|
||||
_(GetProp_InlineAccess) \
|
||||
_(GetProp_Innerize) \
|
||||
_(GetProp_InlineCache) \
|
||||
_(GetProp_SharedCache) \
|
||||
_(GetProp_ModuleNamespace) \
|
||||
\
|
||||
_(SetProp_CommonSetter) \
|
||||
_(SetProp_TypedObject) \
|
||||
_(SetProp_DefiniteSlot) \
|
||||
_(SetProp_Unboxed) \
|
||||
_(SetProp_InlineAccess) \
|
||||
_(SetProp_InlineCache) \
|
||||
\
|
||||
_(GetElem_TypedObject) \
|
||||
_(GetElem_Dense) \
|
||||
_(GetElem_TypedStatic) \
|
||||
_(GetElem_TypedArray) \
|
||||
_(GetElem_String) \
|
||||
_(GetElem_Arguments) \
|
||||
_(GetElem_ArgumentsInlined) \
|
||||
_(GetElem_InlineCache) \
|
||||
\
|
||||
_(SetElem_TypedObject) \
|
||||
_(SetElem_TypedStatic) \
|
||||
_(SetElem_TypedArray) \
|
||||
_(SetElem_Dense) \
|
||||
_(SetElem_Arguments) \
|
||||
_(SetElem_InlineCache) \
|
||||
\
|
||||
_(BinaryArith_Concat) \
|
||||
_(BinaryArith_SpecializedTypes) \
|
||||
_(BinaryArith_SpecializedOnBaselineTypes) \
|
||||
_(BinaryArith_SharedCache) \
|
||||
_(BinaryArith_Call) \
|
||||
\
|
||||
_(InlineCache_OptimizedStub) \
|
||||
\
|
||||
_(Call_Inline)
|
||||
|
||||
|
||||
// Ordering is important below. All outcomes before GenericSuccess will be
|
||||
// considered failures, and all outcomes after GenericSuccess will be
|
||||
// considered successes.
|
||||
#define TRACKED_OUTCOME_LIST(_) \
|
||||
_(GenericFailure) \
|
||||
_(Disabled) \
|
||||
_(NoTypeInfo) \
|
||||
_(NoAnalysisInfo) \
|
||||
_(NoShapeInfo) \
|
||||
_(UnknownObject) \
|
||||
_(UnknownProperties) \
|
||||
_(Singleton) \
|
||||
_(NotSingleton) \
|
||||
_(NotFixedSlot) \
|
||||
_(InconsistentFixedSlot) \
|
||||
_(NotObject) \
|
||||
_(NotStruct) \
|
||||
_(NotUnboxed) \
|
||||
_(NotUndefined) \
|
||||
_(UnboxedConvertedToNative) \
|
||||
_(StructNoField) \
|
||||
_(InconsistentFieldType) \
|
||||
_(InconsistentFieldOffset) \
|
||||
_(NeedsTypeBarrier) \
|
||||
_(InDictionaryMode) \
|
||||
_(NoProtoFound) \
|
||||
_(MultiProtoPaths) \
|
||||
_(NonWritableProperty) \
|
||||
_(ProtoIndexedProps) \
|
||||
_(ArrayBadFlags) \
|
||||
_(ArrayDoubleConversion) \
|
||||
_(ArrayRange) \
|
||||
_(ArraySeenNegativeIndex) \
|
||||
_(TypedObjectHasDetachedBuffer) \
|
||||
_(TypedObjectArrayRange) \
|
||||
_(AccessNotDense) \
|
||||
_(AccessNotSimdObject) \
|
||||
_(AccessNotTypedObject) \
|
||||
_(AccessNotTypedArray) \
|
||||
_(AccessNotString) \
|
||||
_(OperandNotString) \
|
||||
_(OperandNotNumber) \
|
||||
_(OperandNotStringOrNumber) \
|
||||
_(OperandNotSimpleArith) \
|
||||
_(StaticTypedArrayUint32) \
|
||||
_(StaticTypedArrayCantComputeMask) \
|
||||
_(OutOfBounds) \
|
||||
_(GetElemStringNotCached) \
|
||||
_(NonNativeReceiver) \
|
||||
_(IndexType) \
|
||||
_(SetElemNonDenseNonTANotCached) \
|
||||
_(NoSimdJitSupport) \
|
||||
_(SimdTypeNotOptimized) \
|
||||
_(UnknownSimdProperty) \
|
||||
_(NotModuleNamespace) \
|
||||
_(UnknownProperty) \
|
||||
\
|
||||
_(ICOptStub_GenericSuccess) \
|
||||
\
|
||||
_(ICGetPropStub_ReadSlot) \
|
||||
_(ICGetPropStub_CallGetter) \
|
||||
_(ICGetPropStub_ArrayLength) \
|
||||
_(ICGetPropStub_UnboxedRead) \
|
||||
_(ICGetPropStub_UnboxedReadExpando) \
|
||||
_(ICGetPropStub_UnboxedArrayLength) \
|
||||
_(ICGetPropStub_TypedArrayLength) \
|
||||
_(ICGetPropStub_DOMProxyShadowed) \
|
||||
_(ICGetPropStub_DOMProxyUnshadowed) \
|
||||
_(ICGetPropStub_GenericProxy) \
|
||||
_(ICGetPropStub_ArgumentsLength) \
|
||||
\
|
||||
_(ICSetPropStub_Slot) \
|
||||
_(ICSetPropStub_GenericProxy) \
|
||||
_(ICSetPropStub_DOMProxyShadowed) \
|
||||
_(ICSetPropStub_DOMProxyUnshadowed) \
|
||||
_(ICSetPropStub_CallSetter) \
|
||||
_(ICSetPropStub_AddSlot) \
|
||||
_(ICSetPropStub_SetUnboxed) \
|
||||
\
|
||||
_(ICGetElemStub_ReadSlot) \
|
||||
_(ICGetElemStub_CallGetter) \
|
||||
_(ICGetElemStub_ReadUnboxed) \
|
||||
_(ICGetElemStub_Dense) \
|
||||
_(ICGetElemStub_DenseHole) \
|
||||
_(ICGetElemStub_TypedArray) \
|
||||
_(ICGetElemStub_ArgsElementMapped) \
|
||||
_(ICGetElemStub_ArgsElementUnmapped) \
|
||||
\
|
||||
_(ICSetElemStub_Dense) \
|
||||
_(ICSetElemStub_TypedArray) \
|
||||
\
|
||||
_(ICNameStub_ReadSlot) \
|
||||
_(ICNameStub_CallGetter) \
|
||||
_(ICNameStub_TypeOfNoProperty) \
|
||||
\
|
||||
_(CantInlineGeneric) \
|
||||
_(CantInlineNoTarget) \
|
||||
_(CantInlineNotInterpreted) \
|
||||
_(CantInlineNoBaseline) \
|
||||
_(CantInlineLazy) \
|
||||
_(CantInlineNotConstructor) \
|
||||
_(CantInlineClassConstructor) \
|
||||
_(CantInlineDisabledIon) \
|
||||
_(CantInlineTooManyArgs) \
|
||||
_(CantInlineNeedsArgsObj) \
|
||||
_(CantInlineDebuggee) \
|
||||
_(CantInlineUnknownProps) \
|
||||
_(CantInlineExceededDepth) \
|
||||
_(CantInlineExceededTotalBytecodeLength) \
|
||||
_(CantInlineBigCaller) \
|
||||
_(CantInlineBigCallee) \
|
||||
_(CantInlineBigCalleeInlinedBytecodeLength) \
|
||||
_(CantInlineNotHot) \
|
||||
_(CantInlineNotInDispatch) \
|
||||
_(CantInlineUnreachable) \
|
||||
_(CantInlineNativeBadForm) \
|
||||
_(CantInlineNativeBadType) \
|
||||
_(CantInlineNativeNoTemplateObj) \
|
||||
_(CantInlineBound) \
|
||||
_(CantInlineNativeNoSpecialization) \
|
||||
_(HasCommonInliningPath) \
|
||||
\
|
||||
_(GenericSuccess) \
|
||||
_(Inlined) \
|
||||
_(DOM) \
|
||||
_(Monomorphic) \
|
||||
_(Polymorphic)
|
||||
|
||||
#define TRACKED_TYPESITE_LIST(_) \
|
||||
_(Receiver) \
|
||||
_(Operand) \
|
||||
_(Index) \
|
||||
_(Value) \
|
||||
_(Call_Target) \
|
||||
_(Call_This) \
|
||||
_(Call_Arg) \
|
||||
_(Call_Return)
|
||||
|
||||
enum class TrackedStrategy : uint32_t {
|
||||
#define STRATEGY_OP(name) name,
|
||||
TRACKED_STRATEGY_LIST(STRATEGY_OP)
|
||||
#undef STRATEGY_OPT
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
enum class TrackedOutcome : uint32_t {
|
||||
#define OUTCOME_OP(name) name,
|
||||
TRACKED_OUTCOME_LIST(OUTCOME_OP)
|
||||
#undef OUTCOME_OP
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
enum class TrackedTypeSite : uint32_t {
|
||||
#define TYPESITE_OP(name) name,
|
||||
TRACKED_TYPESITE_LIST(TYPESITE_OP)
|
||||
#undef TYPESITE_OP
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(const char*)
|
||||
TrackedStrategyString(TrackedStrategy strategy);
|
||||
|
||||
JS_PUBLIC_API(const char*)
|
||||
TrackedOutcomeString(TrackedOutcome outcome);
|
||||
|
||||
JS_PUBLIC_API(const char*)
|
||||
TrackedTypeSiteString(TrackedTypeSite site);
|
||||
|
||||
struct ForEachTrackedOptimizationAttemptOp
|
||||
{
|
||||
virtual void operator()(TrackedStrategy strategy, TrackedOutcome outcome) = 0;
|
||||
};
|
||||
|
||||
struct ForEachTrackedOptimizationTypeInfoOp
|
||||
{
|
||||
// Called 0+ times per entry, once for each type in the type set that Ion
|
||||
// saw during MIR construction. readType is always called _before_
|
||||
// operator() on the same entry.
|
||||
//
|
||||
// The keyedBy parameter describes how the type is keyed:
|
||||
// - "primitive" for primitive types
|
||||
// - "constructor" for object types tied to a scripted constructor
|
||||
// function.
|
||||
// - "alloc site" for object types tied to an allocation site.
|
||||
// - "prototype" for object types tied neither to a constructor nor
|
||||
// to an allocation site, but to a prototype.
|
||||
// - "singleton" for object types which only has a single value.
|
||||
// - "function" for object types referring to scripted functions.
|
||||
// - "native" for object types referring to native functions.
|
||||
//
|
||||
// The name parameter is the string representation of the type. If the
|
||||
// type is keyed by "constructor", or if the type itself refers to a
|
||||
// scripted function, the name is the function's displayAtom. If the type
|
||||
// is keyed by "native", this is nullptr.
|
||||
//
|
||||
// The location parameter is the filename if the type is keyed by
|
||||
// "constructor", "alloc site", or if the type itself refers to a scripted
|
||||
// function. If the type is keyed by "native", it is the offset of the
|
||||
// native function, suitable for use with addr2line on Linux or atos on OS
|
||||
// X. Otherwise it is nullptr.
|
||||
//
|
||||
// The lineno parameter is the line number if the type is keyed by
|
||||
// "constructor", "alloc site", or if the type itself refers to a scripted
|
||||
// function. Otherwise it is Nothing().
|
||||
//
|
||||
// The location parameter is the only one that may need escaping if being
|
||||
// quoted.
|
||||
virtual void readType(const char* keyedBy, const char* name,
|
||||
const char* location, mozilla::Maybe<unsigned> lineno) = 0;
|
||||
|
||||
// Called once per entry.
|
||||
virtual void operator()(TrackedTypeSite site, const char* mirType) = 0;
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_TrackedOptimizationInfo_h
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
// This file contains public type declarations that are used *frequently*. If
|
||||
// it doesn't occur at least 10 times in Gecko, it probably shouldn't be in
|
||||
// here.
|
||||
//
|
||||
// It includes only:
|
||||
// - forward declarations of structs and classes;
|
||||
// - typedefs;
|
||||
// - enums (maybe).
|
||||
// It does *not* contain any struct or class definitions.
|
||||
|
||||
#ifndef js_TypeDecls_h
|
||||
#define js_TypeDecls_h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "js-config.h"
|
||||
|
||||
struct JSContext;
|
||||
class JSFunction;
|
||||
class JSObject;
|
||||
class JSScript;
|
||||
class JSString;
|
||||
class JSAddonId;
|
||||
|
||||
struct jsid;
|
||||
|
||||
namespace JS {
|
||||
|
||||
typedef unsigned char Latin1Char;
|
||||
|
||||
class Symbol;
|
||||
class Value;
|
||||
template <typename T> class Handle;
|
||||
template <typename T> class MutableHandle;
|
||||
template <typename T> class Rooted;
|
||||
template <typename T> class PersistentRooted;
|
||||
|
||||
typedef Handle<JSFunction*> HandleFunction;
|
||||
typedef Handle<jsid> HandleId;
|
||||
typedef Handle<JSObject*> HandleObject;
|
||||
typedef Handle<JSScript*> HandleScript;
|
||||
typedef Handle<JSString*> HandleString;
|
||||
typedef Handle<JS::Symbol*> HandleSymbol;
|
||||
typedef Handle<Value> HandleValue;
|
||||
|
||||
typedef MutableHandle<JSFunction*> MutableHandleFunction;
|
||||
typedef MutableHandle<jsid> MutableHandleId;
|
||||
typedef MutableHandle<JSObject*> MutableHandleObject;
|
||||
typedef MutableHandle<JSScript*> MutableHandleScript;
|
||||
typedef MutableHandle<JSString*> MutableHandleString;
|
||||
typedef MutableHandle<JS::Symbol*> MutableHandleSymbol;
|
||||
typedef MutableHandle<Value> MutableHandleValue;
|
||||
|
||||
typedef Rooted<JSObject*> RootedObject;
|
||||
typedef Rooted<JSFunction*> RootedFunction;
|
||||
typedef Rooted<JSScript*> RootedScript;
|
||||
typedef Rooted<JSString*> RootedString;
|
||||
typedef Rooted<JS::Symbol*> RootedSymbol;
|
||||
typedef Rooted<jsid> RootedId;
|
||||
typedef Rooted<JS::Value> RootedValue;
|
||||
|
||||
typedef PersistentRooted<JSFunction*> PersistentRootedFunction;
|
||||
typedef PersistentRooted<jsid> PersistentRootedId;
|
||||
typedef PersistentRooted<JSObject*> PersistentRootedObject;
|
||||
typedef PersistentRooted<JSScript*> PersistentRootedScript;
|
||||
typedef PersistentRooted<JSString*> PersistentRootedString;
|
||||
typedef PersistentRooted<JS::Symbol*> PersistentRootedSymbol;
|
||||
typedef PersistentRooted<Value> PersistentRootedValue;
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_TypeDecls_h */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,244 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_UbiNodeBreadthFirst_h
|
||||
#define js_UbiNodeBreadthFirst_h
|
||||
|
||||
#include "js/UbiNode.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
|
||||
// A breadth-first traversal template for graphs of ubi::Nodes.
|
||||
//
|
||||
// No GC may occur while an instance of this template is live.
|
||||
//
|
||||
// The provided Handler type should have two members:
|
||||
//
|
||||
// typename NodeData;
|
||||
//
|
||||
// The value type of |BreadthFirst<Handler>::visited|, the HashMap of
|
||||
// ubi::Nodes that have been visited so far. Since the algorithm needs a
|
||||
// hash table like this for its own use anyway, it is simple to let
|
||||
// Handler store its own metadata about each node in the same table.
|
||||
//
|
||||
// For example, if you want to find a shortest path to each node from any
|
||||
// traversal starting point, your |NodeData| type could record the first
|
||||
// edge to reach each node, and the node from which it originates. Then,
|
||||
// when the traversal is complete, you can walk backwards from any node
|
||||
// to some starting point, and the path recorded will be a shortest path.
|
||||
//
|
||||
// This type must have a default constructor. If this type owns any other
|
||||
// resources, move constructors and assignment operators are probably a
|
||||
// good idea, too.
|
||||
//
|
||||
// bool operator() (BreadthFirst& traversal,
|
||||
// Node origin, const Edge& edge,
|
||||
// Handler::NodeData* referentData, bool first);
|
||||
//
|
||||
// The visitor function, called to report that we have traversed
|
||||
// |edge| from |origin|. This is called once for each edge we traverse.
|
||||
// As this is a breadth-first search, any prior calls to the visitor function
|
||||
// were for origin nodes not further from the start nodes than |origin|.
|
||||
//
|
||||
// |traversal| is this traversal object, passed along for convenience.
|
||||
//
|
||||
// |referentData| is a pointer to the value of the entry in
|
||||
// |traversal.visited| for |edge.referent|; the visitor function can
|
||||
// store whatever metadata it likes about |edge.referent| there.
|
||||
//
|
||||
// |first| is true if this is the first time we have visited an edge
|
||||
// leading to |edge.referent|. This could be stored in NodeData, but
|
||||
// the algorithm knows whether it has just created the entry in
|
||||
// |traversal.visited|, so it passes it along for convenience.
|
||||
//
|
||||
// The visitor function may call |traversal.abandonReferent()| if it
|
||||
// doesn't want to traverse the outgoing edges of |edge.referent|. You can
|
||||
// use this to limit the traversal to a given portion of the graph: it will
|
||||
// never visit nodes reachable only through nodes that you have abandoned.
|
||||
// Note that |abandonReferent| must be called the first time the given node
|
||||
// is reached; that is, |first| must be true.
|
||||
//
|
||||
// The visitor function may call |traversal.stop()| if it doesn't want
|
||||
// to visit any more nodes at all.
|
||||
//
|
||||
// The visitor function may consult |traversal.visited| for information
|
||||
// about other nodes, but it should not add or remove entries.
|
||||
//
|
||||
// The visitor function should return true on success, or false if an
|
||||
// error occurs. A false return value terminates the traversal
|
||||
// immediately, and causes BreadthFirst<Handler>::traverse to return
|
||||
// false.
|
||||
template<typename Handler>
|
||||
struct BreadthFirst {
|
||||
|
||||
// Construct a breadth-first traversal object that reports the nodes it
|
||||
// reaches to |handler|. The traversal asserts that no GC happens in its
|
||||
// runtime during its lifetime.
|
||||
//
|
||||
// We do nothing with noGC, other than require it to exist, with a lifetime
|
||||
// that encloses our own.
|
||||
BreadthFirst(JSContext* cx, Handler& handler, const JS::AutoCheckCannotGC& noGC)
|
||||
: wantNames(true), cx(cx), visited(), handler(handler), pending(),
|
||||
traversalBegun(false), stopRequested(false), abandonRequested(false)
|
||||
{ }
|
||||
|
||||
// Initialize this traversal object. Return false on OOM.
|
||||
bool init() { return visited.init(); }
|
||||
|
||||
// Add |node| as a starting point for the traversal. You may add
|
||||
// as many starting points as you like. Return false on OOM.
|
||||
bool addStart(Node node) { return pending.append(node); }
|
||||
|
||||
// Add |node| as a starting point for the traversal (see addStart) and also
|
||||
// add it to the |visited| set. Return false on OOM.
|
||||
bool addStartVisited(Node node) {
|
||||
typename NodeMap::AddPtr ptr = visited.lookupForAdd(node);
|
||||
if (!ptr && !visited.add(ptr, node, typename Handler::NodeData()))
|
||||
return false;
|
||||
return addStart(node);
|
||||
}
|
||||
|
||||
// True if the handler wants us to compute edge names; doing so can be
|
||||
// expensive in time and memory. True by default.
|
||||
bool wantNames;
|
||||
|
||||
// Traverse the graph in breadth-first order, starting at the given
|
||||
// start nodes, applying |handler::operator()| for each edge traversed
|
||||
// as described above.
|
||||
//
|
||||
// This should be called only once per instance of this class.
|
||||
//
|
||||
// Return false on OOM or error return from |handler::operator()|.
|
||||
bool traverse()
|
||||
{
|
||||
MOZ_ASSERT(!traversalBegun);
|
||||
traversalBegun = true;
|
||||
|
||||
// While there are pending nodes, visit them.
|
||||
while (!pending.empty()) {
|
||||
Node origin = pending.front();
|
||||
pending.popFront();
|
||||
|
||||
// Get a range containing all origin's outgoing edges.
|
||||
auto range = origin.edges(cx, wantNames);
|
||||
if (!range)
|
||||
return false;
|
||||
|
||||
// Traverse each edge.
|
||||
for (; !range->empty(); range->popFront()) {
|
||||
MOZ_ASSERT(!stopRequested);
|
||||
|
||||
Edge& edge = range->front();
|
||||
typename NodeMap::AddPtr a = visited.lookupForAdd(edge.referent);
|
||||
bool first = !a;
|
||||
|
||||
if (first) {
|
||||
// This is the first time we've reached |edge.referent|.
|
||||
// Mark it as visited.
|
||||
if (!visited.add(a, edge.referent, typename Handler::NodeData()))
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(a);
|
||||
|
||||
// Report this edge to the visitor function.
|
||||
if (!handler(*this, origin, edge, &a->value(), first))
|
||||
return false;
|
||||
|
||||
if (stopRequested)
|
||||
return true;
|
||||
|
||||
// Arrange to traverse this edge's referent's outgoing edges
|
||||
// later --- unless |handler| asked us not to.
|
||||
if (abandonRequested) {
|
||||
// Skip the enqueue; reset flag for future iterations.
|
||||
abandonRequested = false;
|
||||
} else if (first) {
|
||||
if (!pending.append(edge.referent))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stop traversal, and return true from |traverse| without visiting any
|
||||
// more nodes. Only |handler::operator()| should call this function; it
|
||||
// may do so to stop the traversal early, without returning false and
|
||||
// then making |traverse|'s caller disambiguate that result from a real
|
||||
// error.
|
||||
void stop() { stopRequested = true; }
|
||||
|
||||
// Request that the current edge's referent's outgoing edges not be
|
||||
// traversed. This must be called the first time that referent is reached.
|
||||
// Other edges *to* that referent will still be traversed.
|
||||
void abandonReferent() { abandonRequested = true; }
|
||||
|
||||
// The context with which we were constructed.
|
||||
JSContext* cx;
|
||||
|
||||
// A map associating each node N that we have reached with a
|
||||
// Handler::NodeData, for |handler|'s use. This is public, so that
|
||||
// |handler| can access it to see the traversal thus far.
|
||||
using NodeMap = js::HashMap<Node, typename Handler::NodeData, js::DefaultHasher<Node>,
|
||||
js::SystemAllocPolicy>;
|
||||
NodeMap visited;
|
||||
|
||||
private:
|
||||
// Our handler object.
|
||||
Handler& handler;
|
||||
|
||||
// A queue template. Appending and popping the front are constant time.
|
||||
// Wasted space is never more than some recent actual population plus the
|
||||
// current population.
|
||||
template <typename T>
|
||||
class Queue {
|
||||
js::Vector<T, 0, js::SystemAllocPolicy> head, tail;
|
||||
size_t frontIndex;
|
||||
public:
|
||||
Queue() : head(), tail(), frontIndex(0) { }
|
||||
bool empty() { return frontIndex >= head.length(); }
|
||||
T& front() {
|
||||
MOZ_ASSERT(!empty());
|
||||
return head[frontIndex];
|
||||
}
|
||||
void popFront() {
|
||||
MOZ_ASSERT(!empty());
|
||||
frontIndex++;
|
||||
if (frontIndex >= head.length()) {
|
||||
head.clearAndFree();
|
||||
head.swap(tail);
|
||||
frontIndex = 0;
|
||||
}
|
||||
}
|
||||
bool append(const T& elt) {
|
||||
return frontIndex == 0 ? head.append(elt) : tail.append(elt);
|
||||
}
|
||||
};
|
||||
|
||||
// A queue of nodes that we have reached, but whose outgoing edges we
|
||||
// have not yet traversed. Nodes reachable in fewer edges are enqueued
|
||||
// earlier.
|
||||
Queue<Node> pending;
|
||||
|
||||
// True if our traverse function has been called.
|
||||
bool traversalBegun;
|
||||
|
||||
// True if we've been asked to stop the traversal.
|
||||
bool stopRequested;
|
||||
|
||||
// True if we've been asked to abandon the current edge's referent.
|
||||
bool abandonRequested;
|
||||
};
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_UbiNodeBreadthFirst_h
|
||||
|
|
@ -1,251 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_UbiNodeCensus_h
|
||||
#define js_UbiNodeCensus_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "js/UbiNode.h"
|
||||
#include "js/UbiNodeBreadthFirst.h"
|
||||
|
||||
// A census is a ubi::Node traversal that assigns each node to one or more
|
||||
// buckets, and returns a report with the size of each bucket.
|
||||
//
|
||||
// We summarize the results of a census with counts broken down according to
|
||||
// criteria selected by the API consumer code that is requesting the census. For
|
||||
// example, the following breakdown might give an interesting overview of the
|
||||
// heap:
|
||||
//
|
||||
// - all nodes
|
||||
// - objects
|
||||
// - objects with a specific [[Class]] *
|
||||
// - strings
|
||||
// - scripts
|
||||
// - all other Node types
|
||||
// - nodes with a specific ubi::Node::typeName *
|
||||
//
|
||||
// Obviously, the parts of this tree marked with * represent many separate
|
||||
// counts, depending on how many distinct [[Class]] values and ubi::Node type
|
||||
// names we encounter.
|
||||
//
|
||||
// The supported types of breakdowns are documented in
|
||||
// js/src/doc/Debugger/Debugger.Memory.md.
|
||||
//
|
||||
// When we parse the 'breakdown' argument to takeCensus, we build a tree of
|
||||
// CountType nodes. For example, for the breakdown shown in the
|
||||
// Debugger.Memory.prototype.takeCensus, documentation:
|
||||
//
|
||||
// {
|
||||
// by: "coarseType",
|
||||
// objects: { by: "objectClass" },
|
||||
// other: { by: "internalType" }
|
||||
// }
|
||||
//
|
||||
// we would build the following tree of CountType subclasses:
|
||||
//
|
||||
// ByCoarseType
|
||||
// objects: ByObjectClass
|
||||
// each class: SimpleCount
|
||||
// scripts: SimpleCount
|
||||
// strings: SimpleCount
|
||||
// other: ByUbinodeType
|
||||
// each type: SimpleCount
|
||||
//
|
||||
// The interior nodes are all breakdown types that categorize nodes according to
|
||||
// one characteristic or another; and the leaf nodes are all SimpleType.
|
||||
//
|
||||
// Each CountType has its own concrete C++ type that holds the counts it
|
||||
// produces. SimpleCount::Count just holds totals. ByObjectClass::Count has a
|
||||
// hash table whose keys are object class names and whose values are counts of
|
||||
// some other type (in the example above, SimpleCount).
|
||||
//
|
||||
// To keep actual count nodes small, they have no vtable. Instead, each count
|
||||
// points to its CountType, which knows how to carry out all the operations we
|
||||
// need on a Count. A CountType can produce new count nodes; process nodes as we
|
||||
// visit them; build a JS object reporting the results; and destruct count
|
||||
// nodes.
|
||||
|
||||
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
|
||||
struct Census;
|
||||
|
||||
class CountBase;
|
||||
|
||||
struct CountDeleter {
|
||||
void operator()(CountBase*);
|
||||
};
|
||||
|
||||
using CountBasePtr = js::UniquePtr<CountBase, CountDeleter>;
|
||||
|
||||
// Abstract base class for CountType nodes.
|
||||
struct CountType {
|
||||
explicit CountType() { }
|
||||
virtual ~CountType() { }
|
||||
|
||||
// Destruct a count tree node that this type instance constructed.
|
||||
virtual void destructCount(CountBase& count) = 0;
|
||||
|
||||
// Return a fresh node for the count tree that categorizes nodes according
|
||||
// to this type. Return a nullptr on OOM.
|
||||
virtual CountBasePtr makeCount() = 0;
|
||||
|
||||
// Trace |count| and all its children, for garbage collection.
|
||||
virtual void traceCount(CountBase& count, JSTracer* trc) = 0;
|
||||
|
||||
// Implement the 'count' method for counts returned by this CountType
|
||||
// instance's 'newCount' method.
|
||||
virtual MOZ_MUST_USE bool count(CountBase& count,
|
||||
mozilla::MallocSizeOf mallocSizeOf,
|
||||
const Node& node) = 0;
|
||||
|
||||
// Implement the 'report' method for counts returned by this CountType
|
||||
// instance's 'newCount' method.
|
||||
virtual MOZ_MUST_USE bool report(JSContext* cx, CountBase& count,
|
||||
MutableHandleValue report) = 0;
|
||||
};
|
||||
|
||||
using CountTypePtr = js::UniquePtr<CountType>;
|
||||
|
||||
// An abstract base class for count tree nodes.
|
||||
class CountBase {
|
||||
// In lieu of a vtable, each CountBase points to its type, which
|
||||
// carries not only the implementations of the CountBase methods, but also
|
||||
// additional parameters for the type's behavior, as specified in the
|
||||
// breakdown argument passed to takeCensus.
|
||||
CountType& type;
|
||||
|
||||
protected:
|
||||
~CountBase() { }
|
||||
|
||||
public:
|
||||
explicit CountBase(CountType& type)
|
||||
: type(type)
|
||||
, total_(0)
|
||||
, smallestNodeIdCounted_(SIZE_MAX)
|
||||
{ }
|
||||
|
||||
// Categorize and count |node| as appropriate for this count's type.
|
||||
MOZ_MUST_USE bool count(mozilla::MallocSizeOf mallocSizeOf, const Node& node) {
|
||||
total_++;
|
||||
|
||||
auto id = node.identifier();
|
||||
if (id < smallestNodeIdCounted_) {
|
||||
smallestNodeIdCounted_ = id;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
size_t oldTotal = total_;
|
||||
#endif
|
||||
|
||||
bool ret = type.count(*this, mallocSizeOf, node);
|
||||
|
||||
MOZ_ASSERT(total_ == oldTotal,
|
||||
"CountType::count should not increment total_, CountBase::count handles that");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Construct a JavaScript object reporting the counts recorded in this
|
||||
// count, and store it in |report|. Return true on success, or false on
|
||||
// failure.
|
||||
MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
|
||||
return type.report(cx, *this, report);
|
||||
}
|
||||
|
||||
// Down-cast this CountBase to its true type, based on its 'type' member,
|
||||
// and run its destructor.
|
||||
void destruct() { return type.destructCount(*this); }
|
||||
|
||||
// Trace this count for garbage collection.
|
||||
void trace(JSTracer* trc) { type.traceCount(*this, trc); }
|
||||
|
||||
size_t total_;
|
||||
|
||||
// The smallest JS::ubi::Node::identifier() passed to this instance's
|
||||
// count() method. This provides a stable way to sort sets.
|
||||
Node::Id smallestNodeIdCounted_;
|
||||
};
|
||||
|
||||
class RootedCount : JS::CustomAutoRooter {
|
||||
CountBasePtr count;
|
||||
|
||||
void trace(JSTracer* trc) override { count->trace(trc); }
|
||||
|
||||
public:
|
||||
RootedCount(JSContext* cx, CountBasePtr&& count)
|
||||
: CustomAutoRooter(cx),
|
||||
count(Move(count))
|
||||
{ }
|
||||
CountBase* operator->() const { return count.get(); }
|
||||
explicit operator bool() const { return count.get(); }
|
||||
operator CountBasePtr&() { return count; }
|
||||
};
|
||||
|
||||
// Common data for a census traversal, shared across all CountType nodes.
|
||||
struct Census {
|
||||
JSContext* const cx;
|
||||
// If the targetZones set is non-empty, then only consider nodes whose zone
|
||||
// is an element of the set. If the targetZones set is empty, then nodes in
|
||||
// all zones are considered.
|
||||
JS::ZoneSet targetZones;
|
||||
Zone* atomsZone;
|
||||
|
||||
explicit Census(JSContext* cx) : cx(cx), atomsZone(nullptr) { }
|
||||
|
||||
MOZ_MUST_USE bool init();
|
||||
};
|
||||
|
||||
// A BreadthFirst handler type that conducts a census, using a CountBase to
|
||||
// categorize and count each node.
|
||||
class CensusHandler {
|
||||
Census& census;
|
||||
CountBasePtr& rootCount;
|
||||
mozilla::MallocSizeOf mallocSizeOf;
|
||||
|
||||
public:
|
||||
CensusHandler(Census& census, CountBasePtr& rootCount, mozilla::MallocSizeOf mallocSizeOf)
|
||||
: census(census),
|
||||
rootCount(rootCount),
|
||||
mallocSizeOf(mallocSizeOf)
|
||||
{ }
|
||||
|
||||
MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
|
||||
return rootCount->report(cx, report);
|
||||
}
|
||||
|
||||
// This class needs to retain no per-node data.
|
||||
class NodeData { };
|
||||
|
||||
MOZ_MUST_USE bool operator() (BreadthFirst<CensusHandler>& traversal,
|
||||
Node origin, const Edge& edge,
|
||||
NodeData* referentData, bool first);
|
||||
};
|
||||
|
||||
using CensusTraversal = BreadthFirst<CensusHandler>;
|
||||
|
||||
// Examine the census options supplied by the API consumer, and (among other
|
||||
// things) use that to build a CountType tree.
|
||||
MOZ_MUST_USE bool ParseCensusOptions(JSContext* cx, Census& census, HandleObject options,
|
||||
CountTypePtr& outResult);
|
||||
|
||||
// Parse the breakdown language (as described in
|
||||
// js/src/doc/Debugger/Debugger.Memory.md) into a CountTypePtr. A null pointer
|
||||
// is returned on error and is reported to the cx.
|
||||
CountTypePtr ParseBreakdown(JSContext* cx, HandleValue breakdownValue);
|
||||
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_UbiNodeCensus_h
|
||||
|
|
@ -1,677 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_UbiNodeDominatorTree_h
|
||||
#define js_UbiNodeDominatorTree_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "jsalloc.h"
|
||||
|
||||
#include "js/UbiNode.h"
|
||||
#include "js/UbiNodePostOrder.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
|
||||
/**
|
||||
* In a directed graph with a root node `R`, a node `A` is said to "dominate" a
|
||||
* node `B` iff every path from `R` to `B` contains `A`. A node `A` is said to
|
||||
* be the "immediate dominator" of a node `B` iff it dominates `B`, is not `B`
|
||||
* itself, and does not dominate any other nodes which also dominate `B` in
|
||||
* turn.
|
||||
*
|
||||
* If we take every node from a graph `G` and create a new graph `T` with edges
|
||||
* to each node from its immediate dominator, then `T` is a tree (each node has
|
||||
* only one immediate dominator, or none if it is the root). This tree is called
|
||||
* a "dominator tree".
|
||||
*
|
||||
* This class represents a dominator tree constructed from a `JS::ubi::Node`
|
||||
* heap graph. The domination relationship and dominator trees are useful tools
|
||||
* for analyzing heap graphs because they tell you:
|
||||
*
|
||||
* - Exactly what could be reclaimed by the GC if some node `A` became
|
||||
* unreachable: those nodes which are dominated by `A`,
|
||||
*
|
||||
* - The "retained size" of a node in the heap graph, in contrast to its
|
||||
* "shallow size". The "shallow size" is the space taken by a node itself,
|
||||
* not counting anything it references. The "retained size" of a node is its
|
||||
* shallow size plus the size of all the things that would be collected if
|
||||
* the original node wasn't (directly or indirectly) referencing them. In
|
||||
* other words, the retained size is the shallow size of a node plus the
|
||||
* shallow sizes of every other node it dominates. For example, the root
|
||||
* node in a binary tree might have a small shallow size that does not take
|
||||
* up much space itself, but it dominates the rest of the binary tree and
|
||||
* its retained size is therefore significant (assuming no external
|
||||
* references into the tree).
|
||||
*
|
||||
* The simple, engineered algorithm presented in "A Simple, Fast Dominance
|
||||
* Algorithm" by Cooper el al[0] is used to find dominators and construct the
|
||||
* dominator tree. This algorithm runs in O(n^2) time, but is faster in practice
|
||||
* than alternative algorithms with better theoretical running times, such as
|
||||
* Lengauer-Tarjan which runs in O(e * log(n)). The big caveat to that statement
|
||||
* is that Cooper et al found it is faster in practice *on control flow graphs*
|
||||
* and I'm not convinced that this property also holds on *heap* graphs. That
|
||||
* said, the implementation of this algorithm is *much* simpler than
|
||||
* Lengauer-Tarjan and has been found to be fast enough at least for the time
|
||||
* being.
|
||||
*
|
||||
* [0]: http://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
||||
*/
|
||||
class JS_PUBLIC_API(DominatorTree)
|
||||
{
|
||||
private:
|
||||
// Types.
|
||||
|
||||
using PredecessorSets = js::HashMap<Node, NodeSetPtr, js::DefaultHasher<Node>,
|
||||
js::SystemAllocPolicy>;
|
||||
using NodeToIndexMap = js::HashMap<Node, uint32_t, js::DefaultHasher<Node>,
|
||||
js::SystemAllocPolicy>;
|
||||
class DominatedSets;
|
||||
|
||||
public:
|
||||
class DominatedSetRange;
|
||||
|
||||
/**
|
||||
* A pointer to an immediately dominated node.
|
||||
*
|
||||
* Don't use this type directly; it is no safer than regular pointers. This
|
||||
* is only for use indirectly with range-based for loops and
|
||||
* `DominatedSetRange`.
|
||||
*
|
||||
* @see JS::ubi::DominatorTree::getDominatedSet
|
||||
*/
|
||||
class DominatedNodePtr
|
||||
{
|
||||
friend class DominatedSetRange;
|
||||
|
||||
const JS::ubi::Vector<Node>& postOrder;
|
||||
const uint32_t* ptr;
|
||||
|
||||
DominatedNodePtr(const JS::ubi::Vector<Node>& postOrder, const uint32_t* ptr)
|
||||
: postOrder(postOrder)
|
||||
, ptr(ptr)
|
||||
{ }
|
||||
|
||||
public:
|
||||
bool operator!=(const DominatedNodePtr& rhs) const { return ptr != rhs.ptr; }
|
||||
void operator++() { ptr++; }
|
||||
const Node& operator*() const { return postOrder[*ptr]; }
|
||||
};
|
||||
|
||||
/**
|
||||
* A range of immediately dominated `JS::ubi::Node`s for use with
|
||||
* range-based for loops.
|
||||
*
|
||||
* @see JS::ubi::DominatorTree::getDominatedSet
|
||||
*/
|
||||
class DominatedSetRange
|
||||
{
|
||||
friend class DominatedSets;
|
||||
|
||||
const JS::ubi::Vector<Node>& postOrder;
|
||||
const uint32_t* beginPtr;
|
||||
const uint32_t* endPtr;
|
||||
|
||||
DominatedSetRange(JS::ubi::Vector<Node>& postOrder, const uint32_t* begin, const uint32_t* end)
|
||||
: postOrder(postOrder)
|
||||
, beginPtr(begin)
|
||||
, endPtr(end)
|
||||
{
|
||||
MOZ_ASSERT(begin <= end);
|
||||
}
|
||||
|
||||
public:
|
||||
DominatedNodePtr begin() const {
|
||||
MOZ_ASSERT(beginPtr <= endPtr);
|
||||
return DominatedNodePtr(postOrder, beginPtr);
|
||||
}
|
||||
|
||||
DominatedNodePtr end() const {
|
||||
return DominatedNodePtr(postOrder, endPtr);
|
||||
}
|
||||
|
||||
size_t length() const {
|
||||
MOZ_ASSERT(beginPtr <= endPtr);
|
||||
return endPtr - beginPtr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely skip ahead `n` dominators in the range, in O(1) time.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* mozilla::Maybe<DominatedSetRange> range = myDominatorTree.getDominatedSet(myNode);
|
||||
* if (range.isNothing()) {
|
||||
* // Handle unknown nodes however you see fit...
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* // Don't care about the first ten, for whatever reason.
|
||||
* range->skip(10);
|
||||
* for (const JS::ubi::Node& dominatedNode : *range) {
|
||||
* // ...
|
||||
* }
|
||||
*/
|
||||
void skip(size_t n) {
|
||||
beginPtr += n;
|
||||
if (beginPtr > endPtr)
|
||||
beginPtr = endPtr;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* The set of all dominated sets in a dominator tree.
|
||||
*
|
||||
* Internally stores the sets in a contiguous array, with a side table of
|
||||
* indices into that contiguous array to denote the start index of each
|
||||
* individual set.
|
||||
*/
|
||||
class DominatedSets
|
||||
{
|
||||
JS::ubi::Vector<uint32_t> dominated;
|
||||
JS::ubi::Vector<uint32_t> indices;
|
||||
|
||||
DominatedSets(JS::ubi::Vector<uint32_t>&& dominated, JS::ubi::Vector<uint32_t>&& indices)
|
||||
: dominated(mozilla::Move(dominated))
|
||||
, indices(mozilla::Move(indices))
|
||||
{ }
|
||||
|
||||
public:
|
||||
// DominatedSets is not copy-able.
|
||||
DominatedSets(const DominatedSets& rhs) = delete;
|
||||
DominatedSets& operator=(const DominatedSets& rhs) = delete;
|
||||
|
||||
// DominatedSets is move-able.
|
||||
DominatedSets(DominatedSets&& rhs)
|
||||
: dominated(mozilla::Move(rhs.dominated))
|
||||
, indices(mozilla::Move(rhs.indices))
|
||||
{
|
||||
MOZ_ASSERT(this != &rhs, "self-move not allowed");
|
||||
}
|
||||
DominatedSets& operator=(DominatedSets&& rhs) {
|
||||
this->~DominatedSets();
|
||||
new (this) DominatedSets(mozilla::Move(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DominatedSets given the mapping of a node index to its
|
||||
* immediate dominator. Returns `Some` on success, `Nothing` on OOM
|
||||
* failure.
|
||||
*/
|
||||
static mozilla::Maybe<DominatedSets> Create(const JS::ubi::Vector<uint32_t>& doms) {
|
||||
auto length = doms.length();
|
||||
MOZ_ASSERT(length < UINT32_MAX);
|
||||
|
||||
// Create a vector `dominated` holding a flattened set of buckets of
|
||||
// immediately dominated children nodes, with a lookup table
|
||||
// `indices` mapping from each node to the beginning of its bucket.
|
||||
//
|
||||
// This has three phases:
|
||||
//
|
||||
// 1. Iterate over the full set of nodes and count up the size of
|
||||
// each bucket. These bucket sizes are temporarily stored in the
|
||||
// `indices` vector.
|
||||
//
|
||||
// 2. Convert the `indices` vector to store the cumulative sum of
|
||||
// the sizes of all buckets before each index, resulting in a
|
||||
// mapping from node index to one past the end of that node's
|
||||
// bucket.
|
||||
//
|
||||
// 3. Iterate over the full set of nodes again, filling in bucket
|
||||
// entries from the end of the bucket's range to its
|
||||
// beginning. This decrements each index as a bucket entry is
|
||||
// filled in. After having filled in all of a bucket's entries,
|
||||
// the index points to the start of the bucket.
|
||||
|
||||
JS::ubi::Vector<uint32_t> dominated;
|
||||
JS::ubi::Vector<uint32_t> indices;
|
||||
if (!dominated.growBy(length) || !indices.growBy(length))
|
||||
return mozilla::Nothing();
|
||||
|
||||
// 1
|
||||
memset(indices.begin(), 0, length * sizeof(uint32_t));
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
indices[doms[i]]++;
|
||||
|
||||
// 2
|
||||
uint32_t sumOfSizes = 0;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
sumOfSizes += indices[i];
|
||||
MOZ_ASSERT(sumOfSizes <= length);
|
||||
indices[i] = sumOfSizes;
|
||||
}
|
||||
|
||||
// 3
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
auto idxOfDom = doms[i];
|
||||
indices[idxOfDom]--;
|
||||
dominated[indices[idxOfDom]] = i;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Assert that our buckets are non-overlapping and don't run off the
|
||||
// end of the vector.
|
||||
uint32_t lastIndex = 0;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
MOZ_ASSERT(indices[i] >= lastIndex);
|
||||
MOZ_ASSERT(indices[i] < length);
|
||||
lastIndex = indices[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
return mozilla::Some(DominatedSets(mozilla::Move(dominated), mozilla::Move(indices)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of nodes immediately dominated by the node at
|
||||
* `postOrder[nodeIndex]`.
|
||||
*/
|
||||
DominatedSetRange dominatedSet(JS::ubi::Vector<Node>& postOrder, uint32_t nodeIndex) const {
|
||||
MOZ_ASSERT(postOrder.length() == indices.length());
|
||||
MOZ_ASSERT(nodeIndex < indices.length());
|
||||
auto end = nodeIndex == indices.length() - 1
|
||||
? dominated.end()
|
||||
: &dominated[indices[nodeIndex + 1]];
|
||||
return DominatedSetRange(postOrder, &dominated[indices[nodeIndex]], end);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// Data members.
|
||||
JS::ubi::Vector<Node> postOrder;
|
||||
NodeToIndexMap nodeToPostOrderIndex;
|
||||
JS::ubi::Vector<uint32_t> doms;
|
||||
DominatedSets dominatedSets;
|
||||
mozilla::Maybe<JS::ubi::Vector<JS::ubi::Node::Size>> retainedSizes;
|
||||
|
||||
private:
|
||||
// We use `UNDEFINED` as a sentinel value in the `doms` vector to signal
|
||||
// that we haven't found any dominators for the node at the corresponding
|
||||
// index in `postOrder` yet.
|
||||
static const uint32_t UNDEFINED = UINT32_MAX;
|
||||
|
||||
DominatorTree(JS::ubi::Vector<Node>&& postOrder, NodeToIndexMap&& nodeToPostOrderIndex,
|
||||
JS::ubi::Vector<uint32_t>&& doms, DominatedSets&& dominatedSets)
|
||||
: postOrder(mozilla::Move(postOrder))
|
||||
, nodeToPostOrderIndex(mozilla::Move(nodeToPostOrderIndex))
|
||||
, doms(mozilla::Move(doms))
|
||||
, dominatedSets(mozilla::Move(dominatedSets))
|
||||
, retainedSizes(mozilla::Nothing())
|
||||
{ }
|
||||
|
||||
static uint32_t intersect(JS::ubi::Vector<uint32_t>& doms, uint32_t finger1, uint32_t finger2) {
|
||||
while (finger1 != finger2) {
|
||||
if (finger1 < finger2)
|
||||
finger1 = doms[finger1];
|
||||
else if (finger2 < finger1)
|
||||
finger2 = doms[finger2];
|
||||
}
|
||||
return finger1;
|
||||
}
|
||||
|
||||
// Do the post order traversal of the heap graph and populate our
|
||||
// predecessor sets.
|
||||
static MOZ_MUST_USE bool doTraversal(JSContext* cx, AutoCheckCannotGC& noGC, const Node& root,
|
||||
JS::ubi::Vector<Node>& postOrder,
|
||||
PredecessorSets& predecessorSets) {
|
||||
uint32_t nodeCount = 0;
|
||||
auto onNode = [&](const Node& node) {
|
||||
nodeCount++;
|
||||
if (MOZ_UNLIKELY(nodeCount == UINT32_MAX))
|
||||
return false;
|
||||
return postOrder.append(node);
|
||||
};
|
||||
|
||||
auto onEdge = [&](const Node& origin, const Edge& edge) {
|
||||
auto p = predecessorSets.lookupForAdd(edge.referent);
|
||||
if (!p) {
|
||||
mozilla::UniquePtr<NodeSet, DeletePolicy<NodeSet>> set(js_new<NodeSet>());
|
||||
if (!set ||
|
||||
!set->init() ||
|
||||
!predecessorSets.add(p, edge.referent, mozilla::Move(set)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(p && p->value());
|
||||
return p->value()->put(origin);
|
||||
};
|
||||
|
||||
PostOrder traversal(cx, noGC);
|
||||
return traversal.init() &&
|
||||
traversal.addStart(root) &&
|
||||
traversal.traverse(onNode, onEdge);
|
||||
}
|
||||
|
||||
// Populates the given `map` with an entry for each node to its index in
|
||||
// `postOrder`.
|
||||
static MOZ_MUST_USE bool mapNodesToTheirIndices(JS::ubi::Vector<Node>& postOrder,
|
||||
NodeToIndexMap& map) {
|
||||
MOZ_ASSERT(!map.initialized());
|
||||
MOZ_ASSERT(postOrder.length() < UINT32_MAX);
|
||||
uint32_t length = postOrder.length();
|
||||
if (!map.init(length))
|
||||
return false;
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
map.putNewInfallible(postOrder[i], i);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert the Node -> NodeSet predecessorSets to a index -> Vector<index>
|
||||
// form.
|
||||
static MOZ_MUST_USE bool convertPredecessorSetsToVectors(
|
||||
const Node& root,
|
||||
JS::ubi::Vector<Node>& postOrder,
|
||||
PredecessorSets& predecessorSets,
|
||||
NodeToIndexMap& nodeToPostOrderIndex,
|
||||
JS::ubi::Vector<JS::ubi::Vector<uint32_t>>& predecessorVectors)
|
||||
{
|
||||
MOZ_ASSERT(postOrder.length() < UINT32_MAX);
|
||||
uint32_t length = postOrder.length();
|
||||
|
||||
MOZ_ASSERT(predecessorVectors.length() == 0);
|
||||
if (!predecessorVectors.growBy(length))
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < length - 1; i++) {
|
||||
auto& node = postOrder[i];
|
||||
MOZ_ASSERT(node != root,
|
||||
"Only the last node should be root, since this was a post order traversal.");
|
||||
|
||||
auto ptr = predecessorSets.lookup(node);
|
||||
MOZ_ASSERT(ptr,
|
||||
"Because this isn't the root, it had better have predecessors, or else how "
|
||||
"did we even find it.");
|
||||
|
||||
auto& predecessors = ptr->value();
|
||||
if (!predecessorVectors[i].reserve(predecessors->count()))
|
||||
return false;
|
||||
for (auto range = predecessors->all(); !range.empty(); range.popFront()) {
|
||||
auto ptr = nodeToPostOrderIndex.lookup(range.front());
|
||||
MOZ_ASSERT(ptr);
|
||||
predecessorVectors[i].infallibleAppend(ptr->value());
|
||||
}
|
||||
}
|
||||
predecessorSets.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize `doms` such that the immediate dominator of the `root` is the
|
||||
// `root` itself and all others are `UNDEFINED`.
|
||||
static MOZ_MUST_USE bool initializeDominators(JS::ubi::Vector<uint32_t>& doms,
|
||||
uint32_t length) {
|
||||
MOZ_ASSERT(doms.length() == 0);
|
||||
if (!doms.growByUninitialized(length))
|
||||
return false;
|
||||
doms[length - 1] = length - 1;
|
||||
for (uint32_t i = 0; i < length - 1; i++)
|
||||
doms[i] = UNDEFINED;
|
||||
return true;
|
||||
}
|
||||
|
||||
void assertSanity() const {
|
||||
MOZ_ASSERT(postOrder.length() == doms.length());
|
||||
MOZ_ASSERT(postOrder.length() == nodeToPostOrderIndex.count());
|
||||
MOZ_ASSERT_IF(retainedSizes.isSome(), postOrder.length() == retainedSizes->length());
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool computeRetainedSizes(mozilla::MallocSizeOf mallocSizeOf) {
|
||||
MOZ_ASSERT(retainedSizes.isNothing());
|
||||
auto length = postOrder.length();
|
||||
|
||||
retainedSizes.emplace();
|
||||
if (!retainedSizes->growBy(length)) {
|
||||
retainedSizes = mozilla::Nothing();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate in forward order so that we know all of a node's children in
|
||||
// the dominator tree have already had their retained size
|
||||
// computed. Then we can simply say that the retained size of a node is
|
||||
// its shallow size (JS::ubi::Node::size) plus the retained sizes of its
|
||||
// immediate children in the tree.
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
auto size = postOrder[i].size(mallocSizeOf);
|
||||
|
||||
for (const auto& dominated : dominatedSets.dominatedSet(postOrder, i)) {
|
||||
// The root node dominates itself, but shouldn't contribute to
|
||||
// its own retained size.
|
||||
if (dominated == postOrder[length - 1]) {
|
||||
MOZ_ASSERT(i == length - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ptr = nodeToPostOrderIndex.lookup(dominated);
|
||||
MOZ_ASSERT(ptr);
|
||||
auto idxOfDominated = ptr->value();
|
||||
MOZ_ASSERT(idxOfDominated < i);
|
||||
size += retainedSizes.ref()[idxOfDominated];
|
||||
}
|
||||
|
||||
retainedSizes.ref()[i] = size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
// DominatorTree is not copy-able.
|
||||
DominatorTree(const DominatorTree&) = delete;
|
||||
DominatorTree& operator=(const DominatorTree&) = delete;
|
||||
|
||||
// DominatorTree is move-able.
|
||||
DominatorTree(DominatorTree&& rhs)
|
||||
: postOrder(mozilla::Move(rhs.postOrder))
|
||||
, nodeToPostOrderIndex(mozilla::Move(rhs.nodeToPostOrderIndex))
|
||||
, doms(mozilla::Move(rhs.doms))
|
||||
, dominatedSets(mozilla::Move(rhs.dominatedSets))
|
||||
, retainedSizes(mozilla::Move(rhs.retainedSizes))
|
||||
{
|
||||
MOZ_ASSERT(this != &rhs, "self-move is not allowed");
|
||||
}
|
||||
DominatorTree& operator=(DominatorTree&& rhs) {
|
||||
this->~DominatorTree();
|
||||
new (this) DominatorTree(mozilla::Move(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a `DominatorTree` of the heap graph visible from `root`. The
|
||||
* `root` is also used as the root of the resulting dominator tree.
|
||||
*
|
||||
* The resulting `DominatorTree` instance must not outlive the
|
||||
* `JS::ubi::Node` graph it was constructed from.
|
||||
*
|
||||
* - For `JS::ubi::Node` graphs backed by the live heap graph, this means
|
||||
* that the `DominatorTree`'s lifetime _must_ be contained within the
|
||||
* scope of the provided `AutoCheckCannotGC` reference because a GC will
|
||||
* invalidate the nodes.
|
||||
*
|
||||
* - For `JS::ubi::Node` graphs backed by some other offline structure
|
||||
* provided by the embedder, the resulting `DominatorTree`'s lifetime is
|
||||
* bounded by that offline structure's lifetime.
|
||||
*
|
||||
* In practice, this means that within SpiderMonkey we must treat
|
||||
* `DominatorTree` as if it were backed by the live heap graph and trust
|
||||
* that embedders with knowledge of the graph's implementation will do the
|
||||
* Right Thing.
|
||||
*
|
||||
* Returns `mozilla::Nothing()` on OOM failure. It is the caller's
|
||||
* responsibility to handle and report the OOM.
|
||||
*/
|
||||
static mozilla::Maybe<DominatorTree>
|
||||
Create(JSContext* cx, AutoCheckCannotGC& noGC, const Node& root) {
|
||||
JS::ubi::Vector<Node> postOrder;
|
||||
PredecessorSets predecessorSets;
|
||||
if (!predecessorSets.init() || !doTraversal(cx, noGC, root, postOrder, predecessorSets))
|
||||
return mozilla::Nothing();
|
||||
|
||||
MOZ_ASSERT(postOrder.length() < UINT32_MAX);
|
||||
uint32_t length = postOrder.length();
|
||||
MOZ_ASSERT(postOrder[length - 1] == root);
|
||||
|
||||
// From here on out we wish to avoid hash table lookups, and we use
|
||||
// indices into `postOrder` instead of actual nodes wherever
|
||||
// possible. This greatly improves the performance of this
|
||||
// implementation, but we have to pay a little bit of upfront cost to
|
||||
// convert our data structures to play along first.
|
||||
|
||||
NodeToIndexMap nodeToPostOrderIndex;
|
||||
if (!mapNodesToTheirIndices(postOrder, nodeToPostOrderIndex))
|
||||
return mozilla::Nothing();
|
||||
|
||||
JS::ubi::Vector<JS::ubi::Vector<uint32_t>> predecessorVectors;
|
||||
if (!convertPredecessorSetsToVectors(root, postOrder, predecessorSets, nodeToPostOrderIndex,
|
||||
predecessorVectors))
|
||||
return mozilla::Nothing();
|
||||
|
||||
JS::ubi::Vector<uint32_t> doms;
|
||||
if (!initializeDominators(doms, length))
|
||||
return mozilla::Nothing();
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
|
||||
// Iterate over the non-root nodes in reverse post order.
|
||||
for (uint32_t indexPlusOne = length - 1; indexPlusOne > 0; indexPlusOne--) {
|
||||
MOZ_ASSERT(postOrder[indexPlusOne - 1] != root);
|
||||
|
||||
// Take the intersection of every predecessor's dominator set;
|
||||
// that is the current best guess at the immediate dominator for
|
||||
// this node.
|
||||
|
||||
uint32_t newIDomIdx = UNDEFINED;
|
||||
|
||||
auto& predecessors = predecessorVectors[indexPlusOne - 1];
|
||||
auto range = predecessors.all();
|
||||
for ( ; !range.empty(); range.popFront()) {
|
||||
auto idx = range.front();
|
||||
if (doms[idx] != UNDEFINED) {
|
||||
newIDomIdx = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(newIDomIdx != UNDEFINED,
|
||||
"Because the root is initialized to dominate itself and is the first "
|
||||
"node in every path, there must exist a predecessor to this node that "
|
||||
"also has a dominator.");
|
||||
|
||||
for ( ; !range.empty(); range.popFront()) {
|
||||
auto idx = range.front();
|
||||
if (doms[idx] != UNDEFINED)
|
||||
newIDomIdx = intersect(doms, newIDomIdx, idx);
|
||||
}
|
||||
|
||||
// If the immediate dominator changed, we will have to do
|
||||
// another pass of the outer while loop to continue the forward
|
||||
// dataflow.
|
||||
if (newIDomIdx != doms[indexPlusOne - 1]) {
|
||||
doms[indexPlusOne - 1] = newIDomIdx;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto maybeDominatedSets = DominatedSets::Create(doms);
|
||||
if (maybeDominatedSets.isNothing())
|
||||
return mozilla::Nothing();
|
||||
|
||||
return mozilla::Some(DominatorTree(mozilla::Move(postOrder),
|
||||
mozilla::Move(nodeToPostOrderIndex),
|
||||
mozilla::Move(doms),
|
||||
mozilla::Move(*maybeDominatedSets)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root node for this dominator tree.
|
||||
*/
|
||||
const Node& root() const {
|
||||
return postOrder[postOrder.length() - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the immediate dominator of the given `node`. If `node` was not
|
||||
* reachable from the `root` that this dominator tree was constructed from,
|
||||
* then return the null `JS::ubi::Node`.
|
||||
*/
|
||||
Node getImmediateDominator(const Node& node) const {
|
||||
assertSanity();
|
||||
auto ptr = nodeToPostOrderIndex.lookup(node);
|
||||
if (!ptr)
|
||||
return Node();
|
||||
|
||||
auto idx = ptr->value();
|
||||
MOZ_ASSERT(idx < postOrder.length());
|
||||
return postOrder[doms[idx]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of nodes immediately dominated by the given `node`. If `node`
|
||||
* is not a member of this dominator tree, return `Nothing`.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* mozilla::Maybe<DominatedSetRange> range = myDominatorTree.getDominatedSet(myNode);
|
||||
* if (range.isNothing()) {
|
||||
* // Handle unknown node however you see fit...
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* for (const JS::ubi::Node& dominatedNode : *range) {
|
||||
* // Do something with each immediately dominated node...
|
||||
* }
|
||||
*/
|
||||
mozilla::Maybe<DominatedSetRange> getDominatedSet(const Node& node) {
|
||||
assertSanity();
|
||||
auto ptr = nodeToPostOrderIndex.lookup(node);
|
||||
if (!ptr)
|
||||
return mozilla::Nothing();
|
||||
|
||||
auto idx = ptr->value();
|
||||
MOZ_ASSERT(idx < postOrder.length());
|
||||
return mozilla::Some(dominatedSets.dominatedSet(postOrder, idx));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the retained size of the given `node`. The size is placed in
|
||||
* `outSize`, or 0 if `node` is not a member of the dominator tree. Returns
|
||||
* false on OOM failure, leaving `outSize` unchanged.
|
||||
*/
|
||||
MOZ_MUST_USE bool getRetainedSize(const Node& node, mozilla::MallocSizeOf mallocSizeOf,
|
||||
Node::Size& outSize) {
|
||||
assertSanity();
|
||||
auto ptr = nodeToPostOrderIndex.lookup(node);
|
||||
if (!ptr) {
|
||||
outSize = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (retainedSizes.isNothing() && !computeRetainedSizes(mallocSizeOf))
|
||||
return false;
|
||||
|
||||
auto idx = ptr->value();
|
||||
MOZ_ASSERT(idx < postOrder.length());
|
||||
outSize = retainedSizes.ref()[idx];
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_UbiNodeDominatorTree_h
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_UbiNodePostOrder_h
|
||||
#define js_UbiNodePostOrder_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include "jsalloc.h"
|
||||
|
||||
#include "js/UbiNode.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
|
||||
/**
|
||||
* A post-order depth-first traversal of `ubi::Node` graphs.
|
||||
*
|
||||
* No GC may occur while an instance of `PostOrder` is live.
|
||||
*
|
||||
* The `NodeVisitor` type provided to `PostOrder::traverse` must have the
|
||||
* following member:
|
||||
*
|
||||
* bool operator()(Node& node)
|
||||
*
|
||||
* The node visitor method. This method is called once for each `node`
|
||||
* reachable from the start set in post-order.
|
||||
*
|
||||
* This visitor function should return true on success, or false if an error
|
||||
* occurs. A false return value terminates the traversal immediately, and
|
||||
* causes `PostOrder::traverse` to return false.
|
||||
*
|
||||
* The `EdgeVisitor` type provided to `PostOrder::traverse` must have the
|
||||
* following member:
|
||||
*
|
||||
* bool operator()(Node& origin, Edge& edge)
|
||||
*
|
||||
* The edge visitor method. This method is called once for each outgoing
|
||||
* `edge` from `origin` that is reachable from the start set.
|
||||
*
|
||||
* NB: UNLIKE NODES, THERE IS NO GUARANTEED ORDER IN WHICH EDGES AND THEIR
|
||||
* ORIGINS ARE VISITED!
|
||||
*
|
||||
* This visitor function should return true on success, or false if an error
|
||||
* occurs. A false return value terminates the traversal immediately, and
|
||||
* causes `PostOrder::traverse` to return false.
|
||||
*/
|
||||
struct PostOrder {
|
||||
private:
|
||||
struct OriginAndEdges {
|
||||
Node origin;
|
||||
EdgeVector edges;
|
||||
|
||||
OriginAndEdges(const Node& node, EdgeVector&& edges)
|
||||
: origin(node)
|
||||
, edges(mozilla::Move(edges))
|
||||
{ }
|
||||
|
||||
OriginAndEdges(const OriginAndEdges& rhs) = delete;
|
||||
OriginAndEdges& operator=(const OriginAndEdges& rhs) = delete;
|
||||
|
||||
OriginAndEdges(OriginAndEdges&& rhs)
|
||||
: origin(rhs.origin)
|
||||
, edges(mozilla::Move(rhs.edges))
|
||||
{
|
||||
MOZ_ASSERT(&rhs != this, "self-move disallowed");
|
||||
}
|
||||
|
||||
OriginAndEdges& operator=(OriginAndEdges&& rhs) {
|
||||
this->~OriginAndEdges();
|
||||
new (this) OriginAndEdges(mozilla::Move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
using Stack = js::Vector<OriginAndEdges, 256, js::SystemAllocPolicy>;
|
||||
using Set = js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
|
||||
|
||||
JSContext* cx;
|
||||
Set seen;
|
||||
Stack stack;
|
||||
#ifdef DEBUG
|
||||
bool traversed;
|
||||
#endif
|
||||
|
||||
private:
|
||||
MOZ_MUST_USE bool fillEdgesFromRange(EdgeVector& edges, js::UniquePtr<EdgeRange>& range) {
|
||||
MOZ_ASSERT(range);
|
||||
for ( ; !range->empty(); range->popFront()) {
|
||||
if (!edges.append(mozilla::Move(range->front())))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool pushForTraversing(const Node& node) {
|
||||
EdgeVector edges;
|
||||
auto range = node.edges(cx, /* wantNames */ false);
|
||||
return range &&
|
||||
fillEdgesFromRange(edges, range) &&
|
||||
stack.append(OriginAndEdges(node, mozilla::Move(edges)));
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
// Construct a post-order traversal object.
|
||||
//
|
||||
// The traversal asserts that no GC happens in its runtime during its
|
||||
// lifetime via the `AutoCheckCannotGC&` parameter. We do nothing with it,
|
||||
// other than require it to exist with a lifetime that encloses our own.
|
||||
PostOrder(JSContext* cx, AutoCheckCannotGC&)
|
||||
: cx(cx)
|
||||
, seen()
|
||||
, stack()
|
||||
#ifdef DEBUG
|
||||
, traversed(false)
|
||||
#endif
|
||||
{ }
|
||||
|
||||
// Initialize this traversal object. Return false on OOM.
|
||||
MOZ_MUST_USE bool init() { return seen.init(); }
|
||||
|
||||
// Add `node` as a starting point for the traversal. You may add
|
||||
// as many starting points as you like. Returns false on OOM.
|
||||
MOZ_MUST_USE bool addStart(const Node& node) {
|
||||
if (!seen.put(node))
|
||||
return false;
|
||||
return pushForTraversing(node);
|
||||
}
|
||||
|
||||
// Traverse the graph in post-order, starting with the set of nodes passed
|
||||
// to `addStart` and applying `onNode::operator()` for each node in the
|
||||
// graph and `onEdge::operator()` for each edge in the graph, as described
|
||||
// above.
|
||||
//
|
||||
// This should be called only once per instance of this class.
|
||||
//
|
||||
// Return false on OOM or error return from `onNode::operator()` or
|
||||
// `onEdge::operator()`.
|
||||
template<typename NodeVisitor, typename EdgeVisitor>
|
||||
MOZ_MUST_USE bool traverse(NodeVisitor onNode, EdgeVisitor onEdge) {
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(!traversed, "Can only traverse() once!");
|
||||
traversed = true;
|
||||
#endif
|
||||
|
||||
while (!stack.empty()) {
|
||||
auto& origin = stack.back().origin;
|
||||
auto& edges = stack.back().edges;
|
||||
|
||||
if (edges.empty()) {
|
||||
if (!onNode(origin))
|
||||
return false;
|
||||
stack.popBack();
|
||||
continue;
|
||||
}
|
||||
|
||||
Edge edge = mozilla::Move(edges.back());
|
||||
edges.popBack();
|
||||
|
||||
if (!onEdge(origin, edge))
|
||||
return false;
|
||||
|
||||
auto ptr = seen.lookupForAdd(edge.referent);
|
||||
// We've already seen this node, don't follow its edges.
|
||||
if (ptr)
|
||||
continue;
|
||||
|
||||
// Mark the referent as seen and follow its edges.
|
||||
if (!seen.add(ptr, edge.referent) ||
|
||||
!pushForTraversing(edge.referent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_UbiNodePostOrder_h
|
||||
|
|
@ -1,350 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_UbiNodeShortestPaths_h
|
||||
#define js_UbiNodeShortestPaths_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include "jsalloc.h"
|
||||
|
||||
#include "js/UbiNodeBreadthFirst.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
namespace JS {
|
||||
namespace ubi {
|
||||
|
||||
/**
|
||||
* A back edge along a path in the heap graph.
|
||||
*/
|
||||
struct JS_PUBLIC_API(BackEdge)
|
||||
{
|
||||
private:
|
||||
Node predecessor_;
|
||||
EdgeName name_;
|
||||
|
||||
public:
|
||||
using Ptr = mozilla::UniquePtr<BackEdge, JS::DeletePolicy<BackEdge>>;
|
||||
|
||||
BackEdge() : predecessor_(), name_(nullptr) { }
|
||||
|
||||
MOZ_MUST_USE bool init(const Node& predecessor, Edge& edge) {
|
||||
MOZ_ASSERT(!predecessor_);
|
||||
MOZ_ASSERT(!name_);
|
||||
|
||||
predecessor_ = predecessor;
|
||||
name_ = mozilla::Move(edge.name);
|
||||
return true;
|
||||
}
|
||||
|
||||
BackEdge(const BackEdge&) = delete;
|
||||
BackEdge& operator=(const BackEdge&) = delete;
|
||||
|
||||
BackEdge(BackEdge&& rhs)
|
||||
: predecessor_(rhs.predecessor_)
|
||||
, name_(mozilla::Move(rhs.name_))
|
||||
{
|
||||
MOZ_ASSERT(&rhs != this);
|
||||
}
|
||||
|
||||
BackEdge& operator=(BackEdge&& rhs) {
|
||||
this->~BackEdge();
|
||||
new(this) BackEdge(Move(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Ptr clone() const;
|
||||
|
||||
const EdgeName& name() const { return name_; }
|
||||
EdgeName& name() { return name_; }
|
||||
|
||||
const JS::ubi::Node& predecessor() const { return predecessor_; }
|
||||
};
|
||||
|
||||
/**
|
||||
* A path is a series of back edges from which we discovered a target node.
|
||||
*/
|
||||
using Path = JS::ubi::Vector<BackEdge*>;
|
||||
|
||||
/**
|
||||
* The `JS::ubi::ShortestPaths` type represents a collection of up to N shortest
|
||||
* retaining paths for each of a target set of nodes, starting from the same
|
||||
* root node.
|
||||
*/
|
||||
struct JS_PUBLIC_API(ShortestPaths)
|
||||
{
|
||||
private:
|
||||
// Types, type aliases, and data members.
|
||||
|
||||
using BackEdgeVector = JS::ubi::Vector<BackEdge::Ptr>;
|
||||
using NodeToBackEdgeVectorMap = js::HashMap<Node, BackEdgeVector, js::DefaultHasher<Node>,
|
||||
js::SystemAllocPolicy>;
|
||||
|
||||
struct Handler;
|
||||
using Traversal = BreadthFirst<Handler>;
|
||||
|
||||
/**
|
||||
* A `JS::ubi::BreadthFirst` traversal handler that records back edges for
|
||||
* how we reached each node, allowing us to reconstruct the shortest
|
||||
* retaining paths after the traversal.
|
||||
*/
|
||||
struct Handler
|
||||
{
|
||||
using NodeData = BackEdge;
|
||||
|
||||
ShortestPaths& shortestPaths;
|
||||
size_t totalMaxPathsToRecord;
|
||||
size_t totalPathsRecorded;
|
||||
|
||||
explicit Handler(ShortestPaths& shortestPaths)
|
||||
: shortestPaths(shortestPaths)
|
||||
, totalMaxPathsToRecord(shortestPaths.targets_.count() * shortestPaths.maxNumPaths_)
|
||||
, totalPathsRecorded(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(Traversal& traversal, JS::ubi::Node origin, JS::ubi::Edge& edge,
|
||||
BackEdge* back, bool first)
|
||||
{
|
||||
MOZ_ASSERT(back);
|
||||
MOZ_ASSERT(origin == shortestPaths.root_ || traversal.visited.has(origin));
|
||||
MOZ_ASSERT(totalPathsRecorded < totalMaxPathsToRecord);
|
||||
|
||||
if (first && !back->init(origin, edge))
|
||||
return false;
|
||||
|
||||
if (!shortestPaths.targets_.has(edge.referent))
|
||||
return true;
|
||||
|
||||
// If `first` is true, then we moved the edge's name into `back` in
|
||||
// the above call to `init`. So clone that back edge to get the
|
||||
// correct edge name. If `first` is not true, then our edge name is
|
||||
// still in `edge`. This accounts for the asymmetry between
|
||||
// `back->clone()` in the first branch, and the `init` call in the
|
||||
// second branch.
|
||||
|
||||
if (first) {
|
||||
BackEdgeVector paths;
|
||||
if (!paths.reserve(shortestPaths.maxNumPaths_))
|
||||
return false;
|
||||
auto cloned = back->clone();
|
||||
if (!cloned)
|
||||
return false;
|
||||
paths.infallibleAppend(mozilla::Move(cloned));
|
||||
if (!shortestPaths.paths_.putNew(edge.referent, mozilla::Move(paths)))
|
||||
return false;
|
||||
totalPathsRecorded++;
|
||||
} else {
|
||||
auto ptr = shortestPaths.paths_.lookup(edge.referent);
|
||||
MOZ_ASSERT(ptr,
|
||||
"This isn't the first time we have seen the target node `edge.referent`. "
|
||||
"We should have inserted it into shortestPaths.paths_ the first time we "
|
||||
"saw it.");
|
||||
|
||||
if (ptr->value().length() < shortestPaths.maxNumPaths_) {
|
||||
BackEdge::Ptr thisBackEdge(js_new<BackEdge>());
|
||||
if (!thisBackEdge || !thisBackEdge->init(origin, edge))
|
||||
return false;
|
||||
ptr->value().infallibleAppend(mozilla::Move(thisBackEdge));
|
||||
totalPathsRecorded++;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(totalPathsRecorded <= totalMaxPathsToRecord);
|
||||
if (totalPathsRecorded == totalMaxPathsToRecord)
|
||||
traversal.stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// The maximum number of paths to record for each node.
|
||||
uint32_t maxNumPaths_;
|
||||
|
||||
// The root node we are starting the search from.
|
||||
Node root_;
|
||||
|
||||
// The set of nodes we are searching for paths to.
|
||||
NodeSet targets_;
|
||||
|
||||
// The resulting paths.
|
||||
NodeToBackEdgeVectorMap paths_;
|
||||
|
||||
// Need to keep alive the traversal's back edges so we can walk them later
|
||||
// when the traversal is over when recreating the shortest paths.
|
||||
Traversal::NodeMap backEdges_;
|
||||
|
||||
private:
|
||||
// Private methods.
|
||||
|
||||
ShortestPaths(uint32_t maxNumPaths, const Node& root, NodeSet&& targets)
|
||||
: maxNumPaths_(maxNumPaths)
|
||||
, root_(root)
|
||||
, targets_(mozilla::Move(targets))
|
||||
, paths_()
|
||||
, backEdges_()
|
||||
{
|
||||
MOZ_ASSERT(maxNumPaths_ > 0);
|
||||
MOZ_ASSERT(root_);
|
||||
MOZ_ASSERT(targets_.initialized());
|
||||
}
|
||||
|
||||
bool initialized() const {
|
||||
return targets_.initialized() &&
|
||||
paths_.initialized() &&
|
||||
backEdges_.initialized();
|
||||
}
|
||||
|
||||
public:
|
||||
// Public methods.
|
||||
|
||||
ShortestPaths(ShortestPaths&& rhs)
|
||||
: maxNumPaths_(rhs.maxNumPaths_)
|
||||
, root_(rhs.root_)
|
||||
, targets_(mozilla::Move(rhs.targets_))
|
||||
, paths_(mozilla::Move(rhs.paths_))
|
||||
, backEdges_(mozilla::Move(rhs.backEdges_))
|
||||
{
|
||||
MOZ_ASSERT(this != &rhs, "self-move is not allowed");
|
||||
}
|
||||
|
||||
ShortestPaths& operator=(ShortestPaths&& rhs) {
|
||||
this->~ShortestPaths();
|
||||
new (this) ShortestPaths(mozilla::Move(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShortestPaths(const ShortestPaths&) = delete;
|
||||
ShortestPaths& operator=(const ShortestPaths&) = delete;
|
||||
|
||||
/**
|
||||
* Construct a new `JS::ubi::ShortestPaths`, finding up to `maxNumPaths`
|
||||
* shortest retaining paths for each target node in `targets` starting from
|
||||
* `root`.
|
||||
*
|
||||
* The resulting `ShortestPaths` instance must not outlive the
|
||||
* `JS::ubi::Node` graph it was constructed from.
|
||||
*
|
||||
* - For `JS::ubi::Node` graphs backed by the live heap graph, this means
|
||||
* that the `ShortestPaths`'s lifetime _must_ be contained within the
|
||||
* scope of the provided `AutoCheckCannotGC` reference because a GC will
|
||||
* invalidate the nodes.
|
||||
*
|
||||
* - For `JS::ubi::Node` graphs backed by some other offline structure
|
||||
* provided by the embedder, the resulting `ShortestPaths`'s lifetime is
|
||||
* bounded by that offline structure's lifetime.
|
||||
*
|
||||
* Returns `mozilla::Nothing()` on OOM failure. It is the caller's
|
||||
* responsibility to handle and report the OOM.
|
||||
*/
|
||||
static mozilla::Maybe<ShortestPaths>
|
||||
Create(JSContext* cx, AutoCheckCannotGC& noGC, uint32_t maxNumPaths, const Node& root, NodeSet&& targets) {
|
||||
MOZ_ASSERT(targets.count() > 0);
|
||||
MOZ_ASSERT(maxNumPaths > 0);
|
||||
|
||||
size_t count = targets.count();
|
||||
ShortestPaths paths(maxNumPaths, root, mozilla::Move(targets));
|
||||
if (!paths.paths_.init(count))
|
||||
return mozilla::Nothing();
|
||||
|
||||
Handler handler(paths);
|
||||
Traversal traversal(cx, handler, noGC);
|
||||
traversal.wantNames = true;
|
||||
if (!traversal.init() || !traversal.addStart(root) || !traversal.traverse())
|
||||
return mozilla::Nothing();
|
||||
|
||||
// Take ownership of the back edges we created while traversing the
|
||||
// graph so that we can follow them from `paths_` and don't
|
||||
// use-after-free.
|
||||
paths.backEdges_ = mozilla::Move(traversal.visited);
|
||||
|
||||
MOZ_ASSERT(paths.initialized());
|
||||
return mozilla::Some(mozilla::Move(paths));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a range that iterates over each target node we searched for retaining
|
||||
* paths for. The returned range must not outlive the `ShortestPaths`
|
||||
* instance.
|
||||
*/
|
||||
NodeSet::Range eachTarget() const {
|
||||
MOZ_ASSERT(initialized());
|
||||
return targets_.all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the provided functor/lambda/callable once for each retaining path
|
||||
* discovered for `target`. The `func` is passed a single `JS::ubi::Path&`
|
||||
* argument, which contains each edge along the path ordered starting from
|
||||
* the root and ending at the target, and must not outlive the scope of the
|
||||
* call.
|
||||
*
|
||||
* Note that it is possible that we did not find any paths from the root to
|
||||
* the given target, in which case `func` will not be invoked.
|
||||
*/
|
||||
template <class Func>
|
||||
MOZ_MUST_USE bool forEachPath(const Node& target, Func func) {
|
||||
MOZ_ASSERT(initialized());
|
||||
MOZ_ASSERT(targets_.has(target));
|
||||
|
||||
auto ptr = paths_.lookup(target);
|
||||
|
||||
// We didn't find any paths to this target, so nothing to do here.
|
||||
if (!ptr)
|
||||
return true;
|
||||
|
||||
MOZ_ASSERT(ptr->value().length() <= maxNumPaths_);
|
||||
|
||||
Path path;
|
||||
for (const auto& backEdge : ptr->value()) {
|
||||
path.clear();
|
||||
|
||||
if (!path.append(backEdge.get()))
|
||||
return false;
|
||||
|
||||
Node here = backEdge->predecessor();
|
||||
MOZ_ASSERT(here);
|
||||
|
||||
while (here != root_) {
|
||||
auto p = backEdges_.lookup(here);
|
||||
MOZ_ASSERT(p);
|
||||
if (!path.append(&p->value()))
|
||||
return false;
|
||||
here = p->value().predecessor();
|
||||
MOZ_ASSERT(here);
|
||||
}
|
||||
|
||||
path.reverse();
|
||||
|
||||
if (!func(path))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
// A helper function to dump the first `maxNumPaths` shortest retaining paths to
|
||||
// `node` from the GC roots. Useful when GC things you expect to have been
|
||||
// reclaimed by the collector haven't been!
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// JSObject* foo = ...;
|
||||
// JS::ubi::dumpPaths(rt, JS::ubi::Node(foo));
|
||||
JS_PUBLIC_API(void)
|
||||
dumpPaths(JSRuntime* rt, Node node, uint32_t maxNumPaths = 10);
|
||||
#endif
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_UbiNodeShortestPaths_h
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_UniquePtr_h
|
||||
#define js_UniquePtr_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
// Replacement for mozilla::UniquePtr that defaults to js::DefaultDelete.
|
||||
template <typename T, typename D = JS::DeletePolicy<T>>
|
||||
using UniquePtr = mozilla::UniquePtr<T, D>;
|
||||
|
||||
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
|
||||
|
||||
// Replacement for mozilla::MakeUnique that correctly calls js_new and produces
|
||||
// a js::UniquePtr.
|
||||
template<typename T, typename... Args>
|
||||
typename detail::UniqueSelector<T>::SingleObject
|
||||
MakeUnique(Args&&... aArgs)
|
||||
{
|
||||
return UniquePtr<T>(js_new<T>(mozilla::Forward<Args>(aArgs)...));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename detail::UniqueSelector<T>::UnknownBound
|
||||
MakeUnique(decltype(sizeof(int)) aN) = delete;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
typename detail::UniqueSelector<T>::KnownBound
|
||||
MakeUnique(Args&&... aArgs) = delete;
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* js_UniquePtr_h */
|
||||
|
|
@ -1,577 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_Utility_h
|
||||
#define js_Utility_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "mozilla/TemplateLib.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef JS_OOM_DO_BACKTRACES
|
||||
#include <execinfo.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
/* The public JS engine namespace. */
|
||||
namespace JS {}
|
||||
|
||||
/* The mozilla-shared reusable template/utility namespace. */
|
||||
namespace mozilla {}
|
||||
|
||||
/* The private JS engine namespace. */
|
||||
namespace js {}
|
||||
|
||||
#define JS_STATIC_ASSERT(cond) static_assert(cond, "JS_STATIC_ASSERT")
|
||||
#define JS_STATIC_ASSERT_IF(cond, expr) MOZ_STATIC_ASSERT_IF(cond, expr, "JS_STATIC_ASSERT_IF")
|
||||
|
||||
extern MOZ_NORETURN MOZ_COLD JS_PUBLIC_API(void)
|
||||
JS_Assert(const char* s, const char* file, int ln);
|
||||
|
||||
/*
|
||||
* Custom allocator support for SpiderMonkey
|
||||
*/
|
||||
#if defined JS_USE_CUSTOM_ALLOCATOR
|
||||
# include "jscustomallocator.h"
|
||||
#else
|
||||
|
||||
namespace js {
|
||||
namespace oom {
|
||||
|
||||
/*
|
||||
* To make testing OOM in certain helper threads more effective,
|
||||
* allow restricting the OOM testing to a certain helper thread
|
||||
* type. This allows us to fail e.g. in off-thread script parsing
|
||||
* without causing an OOM in the main thread first.
|
||||
*/
|
||||
enum ThreadType {
|
||||
THREAD_TYPE_NONE = 0, // 0
|
||||
THREAD_TYPE_MAIN, // 1
|
||||
THREAD_TYPE_ASMJS, // 2
|
||||
THREAD_TYPE_ION, // 3
|
||||
THREAD_TYPE_PARSE, // 4
|
||||
THREAD_TYPE_COMPRESS, // 5
|
||||
THREAD_TYPE_GCHELPER, // 6
|
||||
THREAD_TYPE_GCPARALLEL, // 7
|
||||
THREAD_TYPE_PROMISE_TASK, // 8
|
||||
THREAD_TYPE_MAX // Used to check shell function arguments
|
||||
};
|
||||
|
||||
/*
|
||||
* Getter/Setter functions to encapsulate mozilla::ThreadLocal,
|
||||
* implementation is in jsutil.cpp.
|
||||
*/
|
||||
# if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
|
||||
extern bool InitThreadType(void);
|
||||
extern void SetThreadType(ThreadType);
|
||||
extern JS_PUBLIC_API(uint32_t) GetThreadType(void);
|
||||
# else
|
||||
inline bool InitThreadType(void) { return true; }
|
||||
inline void SetThreadType(ThreadType t) {};
|
||||
inline JS_PUBLIC_API(uint32_t) GetThreadType(void) { return 0; }
|
||||
# endif
|
||||
|
||||
} /* namespace oom */
|
||||
} /* namespace js */
|
||||
|
||||
# if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
|
||||
|
||||
#ifdef JS_OOM_BREAKPOINT
|
||||
static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
|
||||
#define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
|
||||
#else
|
||||
#define JS_OOM_CALL_BP_FUNC() do {} while(0)
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
namespace oom {
|
||||
|
||||
/*
|
||||
* Out of memory testing support. We provide various testing functions to
|
||||
* simulate OOM conditions and so we can test that they are handled correctly.
|
||||
*/
|
||||
|
||||
extern JS_PUBLIC_DATA(uint32_t) targetThread;
|
||||
extern JS_PUBLIC_DATA(uint64_t) maxAllocations;
|
||||
extern JS_PUBLIC_DATA(uint64_t) counter;
|
||||
extern JS_PUBLIC_DATA(bool) failAlways;
|
||||
|
||||
extern void
|
||||
SimulateOOMAfter(uint64_t allocations, uint32_t thread, bool always);
|
||||
|
||||
extern void
|
||||
ResetSimulatedOOM();
|
||||
|
||||
inline bool
|
||||
IsThreadSimulatingOOM()
|
||||
{
|
||||
return js::oom::targetThread && js::oom::targetThread == js::oom::GetThreadType();
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsSimulatedOOMAllocation()
|
||||
{
|
||||
return IsThreadSimulatingOOM() &&
|
||||
(counter == maxAllocations || (counter > maxAllocations && failAlways));
|
||||
}
|
||||
|
||||
inline bool
|
||||
ShouldFailWithOOM()
|
||||
{
|
||||
if (!IsThreadSimulatingOOM())
|
||||
return false;
|
||||
|
||||
counter++;
|
||||
if (IsSimulatedOOMAllocation()) {
|
||||
JS_OOM_CALL_BP_FUNC();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
HadSimulatedOOM() {
|
||||
return counter >= maxAllocations;
|
||||
}
|
||||
|
||||
} /* namespace oom */
|
||||
} /* namespace js */
|
||||
|
||||
# define JS_OOM_POSSIBLY_FAIL() \
|
||||
do { \
|
||||
if (js::oom::ShouldFailWithOOM()) \
|
||||
return nullptr; \
|
||||
} while (0)
|
||||
|
||||
# define JS_OOM_POSSIBLY_FAIL_BOOL() \
|
||||
do { \
|
||||
if (js::oom::ShouldFailWithOOM()) \
|
||||
return false; \
|
||||
} while (0)
|
||||
|
||||
# else
|
||||
|
||||
# define JS_OOM_POSSIBLY_FAIL() do {} while(0)
|
||||
# define JS_OOM_POSSIBLY_FAIL_BOOL() do {} while(0)
|
||||
namespace js {
|
||||
namespace oom {
|
||||
static inline bool IsSimulatedOOMAllocation() { return false; }
|
||||
static inline bool ShouldFailWithOOM() { return false; }
|
||||
} /* namespace oom */
|
||||
} /* namespace js */
|
||||
|
||||
# endif /* DEBUG || JS_OOM_BREAKPOINT */
|
||||
|
||||
namespace js {
|
||||
|
||||
/* Disable OOM testing in sections which are not OOM safe. */
|
||||
struct MOZ_RAII AutoEnterOOMUnsafeRegion
|
||||
{
|
||||
MOZ_NORETURN MOZ_COLD void crash(const char* reason);
|
||||
MOZ_NORETURN MOZ_COLD void crash(size_t size, const char* reason);
|
||||
|
||||
using AnnotateOOMAllocationSizeCallback = void(*)(size_t);
|
||||
static AnnotateOOMAllocationSizeCallback annotateOOMSizeCallback;
|
||||
static void setAnnotateOOMAllocationSizeCallback(AnnotateOOMAllocationSizeCallback callback) {
|
||||
annotateOOMSizeCallback = callback;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
|
||||
AutoEnterOOMUnsafeRegion()
|
||||
: oomEnabled_(oom::IsThreadSimulatingOOM() && oom::maxAllocations != UINT64_MAX),
|
||||
oomAfter_(0)
|
||||
{
|
||||
if (oomEnabled_) {
|
||||
MOZ_ALWAYS_TRUE(owner_.compareExchange(nullptr, this));
|
||||
oomAfter_ = int64_t(oom::maxAllocations) - int64_t(oom::counter);
|
||||
oom::maxAllocations = UINT64_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
~AutoEnterOOMUnsafeRegion() {
|
||||
if (oomEnabled_) {
|
||||
MOZ_ASSERT(oom::maxAllocations == UINT64_MAX);
|
||||
int64_t maxAllocations = int64_t(oom::counter) + oomAfter_;
|
||||
MOZ_ASSERT(maxAllocations >= 0,
|
||||
"alloc count + oom limit exceeds range, your oom limit is probably too large");
|
||||
oom::maxAllocations = uint64_t(maxAllocations);
|
||||
MOZ_ALWAYS_TRUE(owner_.compareExchange(this, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Used to catch concurrent use from other threads.
|
||||
static mozilla::Atomic<AutoEnterOOMUnsafeRegion*> owner_;
|
||||
|
||||
bool oomEnabled_;
|
||||
int64_t oomAfter_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
static inline void* js_malloc(size_t bytes)
|
||||
{
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return malloc(bytes);
|
||||
}
|
||||
|
||||
static inline void* js_calloc(size_t bytes)
|
||||
{
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return calloc(bytes, 1);
|
||||
}
|
||||
|
||||
static inline void* js_calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return calloc(nmemb, size);
|
||||
}
|
||||
|
||||
static inline void* js_realloc(void* p, size_t bytes)
|
||||
{
|
||||
// realloc() with zero size is not portable, as some implementations may
|
||||
// return nullptr on success and free |p| for this. We assume nullptr
|
||||
// indicates failure and that |p| is still valid.
|
||||
MOZ_ASSERT(bytes != 0);
|
||||
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return realloc(p, bytes);
|
||||
}
|
||||
|
||||
static inline void js_free(void* p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
static inline char* js_strdup(const char* s)
|
||||
{
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return strdup(s);
|
||||
}
|
||||
#endif/* JS_USE_CUSTOM_ALLOCATOR */
|
||||
|
||||
#include <new>
|
||||
|
||||
/*
|
||||
* Low-level memory management in SpiderMonkey:
|
||||
*
|
||||
* ** Do not use the standard malloc/free/realloc: SpiderMonkey allows these
|
||||
* to be redefined (via JS_USE_CUSTOM_ALLOCATOR) and Gecko even #define's
|
||||
* these symbols.
|
||||
*
|
||||
* ** Do not use the builtin C++ operator new and delete: these throw on
|
||||
* error and we cannot override them not to.
|
||||
*
|
||||
* Allocation:
|
||||
*
|
||||
* - If the lifetime of the allocation is tied to the lifetime of a GC-thing
|
||||
* (that is, finalizing the GC-thing will free the allocation), call one of
|
||||
* the following functions:
|
||||
*
|
||||
* JSContext::{malloc_,realloc_,calloc_,new_}
|
||||
* JSRuntime::{malloc_,realloc_,calloc_,new_}
|
||||
*
|
||||
* These functions accumulate the number of bytes allocated which is used as
|
||||
* part of the GC-triggering heuristic.
|
||||
*
|
||||
* The difference between the JSContext and JSRuntime versions is that the
|
||||
* cx version reports an out-of-memory error on OOM. (This follows from the
|
||||
* general SpiderMonkey idiom that a JSContext-taking function reports its
|
||||
* own errors.)
|
||||
*
|
||||
* - Otherwise, use js_malloc/js_realloc/js_calloc/js_new
|
||||
*
|
||||
* Deallocation:
|
||||
*
|
||||
* - Ordinarily, use js_free/js_delete.
|
||||
*
|
||||
* - For deallocations during GC finalization, use one of the following
|
||||
* operations on the FreeOp provided to the finalizer:
|
||||
*
|
||||
* FreeOp::{free_,delete_}
|
||||
*
|
||||
* The advantage of these operations is that the memory is batched and freed
|
||||
* on another thread.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Given a class which should provide a 'new' method, add
|
||||
* JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example).
|
||||
*
|
||||
* Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
|
||||
* or the build will break.
|
||||
*/
|
||||
#define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS) \
|
||||
template <class T, typename... Args> \
|
||||
QUALIFIERS T * \
|
||||
NEWNAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
|
||||
void* memory = ALLOCATOR(sizeof(T)); \
|
||||
return MOZ_LIKELY(memory) \
|
||||
? new(memory) T(mozilla::Forward<Args>(args)...) \
|
||||
: nullptr; \
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a class which should provide 'make' methods, add
|
||||
* JS_DECLARE_MAKE_METHODS (see js::MallocProvider for an example). This
|
||||
* method is functionally the same as JS_DECLARE_NEW_METHODS: it just declares
|
||||
* methods that return mozilla::UniquePtr instances that will singly-manage
|
||||
* ownership of the created object.
|
||||
*
|
||||
* Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS,
|
||||
* or the build will break.
|
||||
*/
|
||||
#define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS)\
|
||||
template <class T, typename... Args> \
|
||||
QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
|
||||
MAKENAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
|
||||
T* ptr = NEWNAME<T>(mozilla::Forward<Args>(args)...); \
|
||||
return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \
|
||||
}
|
||||
|
||||
JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Calculate the number of bytes needed to allocate |numElems| contiguous
|
||||
* instances of type |T|. Return false if the calculation overflowed.
|
||||
*/
|
||||
template <typename T>
|
||||
MOZ_MUST_USE inline bool
|
||||
CalculateAllocSize(size_t numElems, size_t* bytesOut)
|
||||
{
|
||||
*bytesOut = numElems * sizeof(T);
|
||||
return (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the number of bytes needed to allocate a single instance of type
|
||||
* |T| followed by |numExtra| contiguous instances of type |Extra|. Return
|
||||
* false if the calculation overflowed.
|
||||
*/
|
||||
template <typename T, typename Extra>
|
||||
MOZ_MUST_USE inline bool
|
||||
CalculateAllocSizeWithExtra(size_t numExtra, size_t* bytesOut)
|
||||
{
|
||||
*bytesOut = sizeof(T) + numExtra * sizeof(Extra);
|
||||
return (numExtra & mozilla::tl::MulOverflowMask<sizeof(Extra)>::value) == 0 &&
|
||||
*bytesOut >= sizeof(T);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
template <class T>
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
js_delete(const T* p)
|
||||
{
|
||||
if (p) {
|
||||
p->~T();
|
||||
js_free(const_cast<T*>(p));
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
js_delete_poison(const T* p)
|
||||
{
|
||||
if (p) {
|
||||
p->~T();
|
||||
memset(const_cast<T*>(p), 0x3B, sizeof(T));
|
||||
js_free(const_cast<T*>(p));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static MOZ_ALWAYS_INLINE T*
|
||||
js_pod_malloc()
|
||||
{
|
||||
return static_cast<T*>(js_malloc(sizeof(T)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static MOZ_ALWAYS_INLINE T*
|
||||
js_pod_calloc()
|
||||
{
|
||||
return static_cast<T*>(js_calloc(sizeof(T)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static MOZ_ALWAYS_INLINE T*
|
||||
js_pod_malloc(size_t numElems)
|
||||
{
|
||||
size_t bytes;
|
||||
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
|
||||
return nullptr;
|
||||
return static_cast<T*>(js_malloc(bytes));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static MOZ_ALWAYS_INLINE T*
|
||||
js_pod_calloc(size_t numElems)
|
||||
{
|
||||
size_t bytes;
|
||||
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
|
||||
return nullptr;
|
||||
return static_cast<T*>(js_calloc(bytes));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static MOZ_ALWAYS_INLINE T*
|
||||
js_pod_realloc(T* prior, size_t oldSize, size_t newSize)
|
||||
{
|
||||
MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
|
||||
size_t bytes;
|
||||
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes)))
|
||||
return nullptr;
|
||||
return static_cast<T*>(js_realloc(prior, bytes));
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
template<typename T>
|
||||
struct ScopedFreePtrTraits
|
||||
{
|
||||
typedef T* type;
|
||||
static T* empty() { return nullptr; }
|
||||
static void release(T* ptr) { js_free(ptr); }
|
||||
};
|
||||
SCOPED_TEMPLATE(ScopedJSFreePtr, ScopedFreePtrTraits)
|
||||
|
||||
template <typename T>
|
||||
struct ScopedDeletePtrTraits : public ScopedFreePtrTraits<T>
|
||||
{
|
||||
static void release(T* ptr) { js_delete(ptr); }
|
||||
};
|
||||
SCOPED_TEMPLATE(ScopedJSDeletePtr, ScopedDeletePtrTraits)
|
||||
|
||||
template <typename T>
|
||||
struct ScopedReleasePtrTraits : public ScopedFreePtrTraits<T>
|
||||
{
|
||||
static void release(T* ptr) { if (ptr) ptr->release(); }
|
||||
};
|
||||
SCOPED_TEMPLATE(ScopedReleasePtr, ScopedReleasePtrTraits)
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
|
||||
template<typename T>
|
||||
struct DeletePolicy
|
||||
{
|
||||
constexpr DeletePolicy() {}
|
||||
|
||||
template<typename U>
|
||||
MOZ_IMPLICIT DeletePolicy(DeletePolicy<U> other,
|
||||
typename mozilla::EnableIf<mozilla::IsConvertible<U*, T*>::value,
|
||||
int>::Type dummy = 0)
|
||||
{}
|
||||
|
||||
void operator()(const T* ptr) {
|
||||
js_delete(const_cast<T*>(ptr));
|
||||
}
|
||||
};
|
||||
|
||||
struct FreePolicy
|
||||
{
|
||||
void operator()(const void* ptr) {
|
||||
js_free(const_cast<void*>(ptr));
|
||||
}
|
||||
};
|
||||
|
||||
typedef mozilla::UniquePtr<char[], JS::FreePolicy> UniqueChars;
|
||||
typedef mozilla::UniquePtr<char16_t[], JS::FreePolicy> UniqueTwoByteChars;
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
/* Integral types for all hash functions. */
|
||||
typedef uint32_t HashNumber;
|
||||
const unsigned HashNumberSizeBits = 32;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* Given a raw hash code, h, return a number that can be used to select a hash
|
||||
* bucket.
|
||||
*
|
||||
* This function aims to produce as uniform an output distribution as possible,
|
||||
* especially in the most significant (leftmost) bits, even though the input
|
||||
* distribution may be highly nonrandom, given the constraints that this must
|
||||
* be deterministic and quick to compute.
|
||||
*
|
||||
* Since the leftmost bits of the result are best, the hash bucket index is
|
||||
* computed by doing ScrambleHashCode(h) / (2^32/N) or the equivalent
|
||||
* right-shift, not ScrambleHashCode(h) % N or the equivalent bit-mask.
|
||||
*
|
||||
* FIXME: OrderedHashTable uses a bit-mask; see bug 775896.
|
||||
*/
|
||||
inline HashNumber
|
||||
ScrambleHashCode(HashNumber h)
|
||||
{
|
||||
/*
|
||||
* Simply returning h would not cause any hash tables to produce wrong
|
||||
* answers. But it can produce pathologically bad performance: The caller
|
||||
* right-shifts the result, keeping only the highest bits. The high bits of
|
||||
* hash codes are very often completely entropy-free. (So are the lowest
|
||||
* bits.)
|
||||
*
|
||||
* So we use Fibonacci hashing, as described in Knuth, The Art of Computer
|
||||
* Programming, 6.4. This mixes all the bits of the input hash code h.
|
||||
*
|
||||
* The value of goldenRatio is taken from the hex
|
||||
* expansion of the golden ratio, which starts 1.9E3779B9....
|
||||
* This value is especially good if values with consecutive hash codes
|
||||
* are stored in a hash table; see Knuth for details.
|
||||
*/
|
||||
static const HashNumber goldenRatio = 0x9E3779B9U;
|
||||
return h * goldenRatio;
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/* sixgill annotation defines */
|
||||
#ifndef HAVE_STATIC_ANNOTATIONS
|
||||
# define HAVE_STATIC_ANNOTATIONS
|
||||
# ifdef XGILL_PLUGIN
|
||||
# define STATIC_PRECONDITION(COND) __attribute__((precondition(#COND)))
|
||||
# define STATIC_PRECONDITION_ASSUME(COND) __attribute__((precondition_assume(#COND)))
|
||||
# define STATIC_POSTCONDITION(COND) __attribute__((postcondition(#COND)))
|
||||
# define STATIC_POSTCONDITION_ASSUME(COND) __attribute__((postcondition_assume(#COND)))
|
||||
# define STATIC_INVARIANT(COND) __attribute__((invariant(#COND)))
|
||||
# define STATIC_INVARIANT_ASSUME(COND) __attribute__((invariant_assume(#COND)))
|
||||
# define STATIC_ASSUME(COND) \
|
||||
JS_BEGIN_MACRO \
|
||||
__attribute__((assume_static(#COND), unused)) \
|
||||
int STATIC_PASTE1(assume_static_, __COUNTER__); \
|
||||
JS_END_MACRO
|
||||
# else /* XGILL_PLUGIN */
|
||||
# define STATIC_PRECONDITION(COND) /* nothing */
|
||||
# define STATIC_PRECONDITION_ASSUME(COND) /* nothing */
|
||||
# define STATIC_POSTCONDITION(COND) /* nothing */
|
||||
# define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */
|
||||
# define STATIC_INVARIANT(COND) /* nothing */
|
||||
# define STATIC_INVARIANT_ASSUME(COND) /* nothing */
|
||||
# define STATIC_ASSUME(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO
|
||||
# endif /* XGILL_PLUGIN */
|
||||
# define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference())
|
||||
#endif /* HAVE_STATIC_ANNOTATIONS */
|
||||
|
||||
#endif /* js_Utility_h */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,45 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_Vector_h
|
||||
#define js_Vector_h
|
||||
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
/* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4345)
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
|
||||
class TempAllocPolicy;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct TypeIsGCThing : mozilla::FalseType
|
||||
{};
|
||||
|
||||
// Uncomment this once we actually can assert it:
|
||||
//template <>
|
||||
//struct TypeIsGCThing<JS::Value> : mozilla::TrueType
|
||||
//{};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T,
|
||||
size_t MinInlineCapacity = 0,
|
||||
class AllocPolicy = TempAllocPolicy,
|
||||
// Don't use this with JS::Value! Use JS::AutoValueVector instead.
|
||||
typename = typename mozilla::EnableIf<!detail::TypeIsGCThing<T>::value>::Type
|
||||
>
|
||||
using Vector = mozilla::Vector<T, MinInlineCapacity, AllocPolicy>;
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* js_Vector_h */
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_WeakMapPtr_h
|
||||
#define js_WeakMapPtr_h
|
||||
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
// A wrapper around the internal C++ representation of SpiderMonkey WeakMaps,
|
||||
// usable outside the engine.
|
||||
//
|
||||
// The supported template specializations are enumerated in WeakMapPtr.cpp. If
|
||||
// you want to use this class for a different key/value combination, add it to
|
||||
// the list and the compiler will generate the relevant machinery.
|
||||
template <typename K, typename V>
|
||||
class JS_PUBLIC_API(WeakMapPtr)
|
||||
{
|
||||
public:
|
||||
WeakMapPtr() : ptr(nullptr) {}
|
||||
bool init(JSContext* cx);
|
||||
bool initialized() { return ptr != nullptr; }
|
||||
void destroy();
|
||||
virtual ~WeakMapPtr() { MOZ_ASSERT(!initialized()); }
|
||||
void trace(JSTracer* tracer);
|
||||
|
||||
V lookup(const K& key);
|
||||
bool put(JSContext* cx, const K& key, const V& value);
|
||||
|
||||
private:
|
||||
void* ptr;
|
||||
|
||||
// WeakMapPtr is neither copyable nor assignable.
|
||||
WeakMapPtr(const WeakMapPtr& wmp) = delete;
|
||||
WeakMapPtr& operator=(const WeakMapPtr& wmp) = delete;
|
||||
};
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
#endif /* js_WeakMapPtr_h */
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/* -*- 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 allocation policies.
|
||||
*
|
||||
* The allocators here are for system memory with lifetimes which are not
|
||||
* managed by the GC. See the comment at the top of vm/MallocProvider.h.
|
||||
*/
|
||||
|
||||
#ifndef jsalloc_h
|
||||
#define jsalloc_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
enum class AllocFunction {
|
||||
Malloc,
|
||||
Calloc,
|
||||
Realloc
|
||||
};
|
||||
|
||||
struct ContextFriendFields;
|
||||
|
||||
/* Policy for using system memory functions and doing no error reporting. */
|
||||
class SystemAllocPolicy
|
||||
{
|
||||
public:
|
||||
template <typename T> T* maybe_pod_malloc(size_t numElems) { return js_pod_malloc<T>(numElems); }
|
||||
template <typename T> T* maybe_pod_calloc(size_t numElems) { return js_pod_calloc<T>(numElems); }
|
||||
template <typename T> T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
|
||||
return js_pod_realloc<T>(p, oldSize, newSize);
|
||||
}
|
||||
template <typename T> T* pod_malloc(size_t numElems) { return maybe_pod_malloc<T>(numElems); }
|
||||
template <typename T> T* pod_calloc(size_t numElems) { return maybe_pod_calloc<T>(numElems); }
|
||||
template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
|
||||
return maybe_pod_realloc<T>(p, oldSize, newSize);
|
||||
}
|
||||
void free_(void* p) { js_free(p); }
|
||||
void reportAllocOverflow() const {}
|
||||
bool checkSimulatedOOM() const {
|
||||
return !js::oom::ShouldFailWithOOM();
|
||||
}
|
||||
};
|
||||
|
||||
class ExclusiveContext;
|
||||
JS_PUBLIC_API(void) ReportOutOfMemory(ExclusiveContext* cxArg);
|
||||
|
||||
/*
|
||||
* Allocation policy that calls the system memory functions and reports errors
|
||||
* to the context. Since the JSContext given on construction is stored for
|
||||
* the lifetime of the container, this policy may only be used for containers
|
||||
* whose lifetime is a shorter than the given JSContext.
|
||||
*
|
||||
* FIXME bug 647103 - rewrite this in terms of temporary allocation functions,
|
||||
* not the system ones.
|
||||
*/
|
||||
class TempAllocPolicy
|
||||
{
|
||||
ContextFriendFields* const cx_;
|
||||
|
||||
/*
|
||||
* Non-inline helper to call JSRuntime::onOutOfMemory with minimal
|
||||
* code bloat.
|
||||
*/
|
||||
JS_FRIEND_API(void*) onOutOfMemory(AllocFunction allocFunc, size_t nbytes,
|
||||
void* reallocPtr = nullptr);
|
||||
|
||||
template <typename T>
|
||||
T* onOutOfMemoryTyped(AllocFunction allocFunc, size_t numElems, void* reallocPtr = nullptr) {
|
||||
size_t bytes;
|
||||
if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes)))
|
||||
return nullptr;
|
||||
return static_cast<T*>(onOutOfMemory(allocFunc, bytes, reallocPtr));
|
||||
}
|
||||
|
||||
public:
|
||||
MOZ_IMPLICIT TempAllocPolicy(JSContext* cx) : cx_((ContextFriendFields*) cx) {} // :(
|
||||
MOZ_IMPLICIT TempAllocPolicy(ContextFriendFields* cx) : cx_(cx) {}
|
||||
|
||||
template <typename T>
|
||||
T* maybe_pod_malloc(size_t numElems) {
|
||||
return js_pod_malloc<T>(numElems);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* maybe_pod_calloc(size_t numElems) {
|
||||
return js_pod_calloc<T>(numElems);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* maybe_pod_realloc(T* prior, size_t oldSize, size_t newSize) {
|
||||
return js_pod_realloc<T>(prior, oldSize, newSize);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* pod_malloc(size_t numElems) {
|
||||
T* p = maybe_pod_malloc<T>(numElems);
|
||||
if (MOZ_UNLIKELY(!p))
|
||||
p = onOutOfMemoryTyped<T>(AllocFunction::Malloc, numElems);
|
||||
return p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* pod_calloc(size_t numElems) {
|
||||
T* p = maybe_pod_calloc<T>(numElems);
|
||||
if (MOZ_UNLIKELY(!p))
|
||||
p = onOutOfMemoryTyped<T>(AllocFunction::Calloc, numElems);
|
||||
return p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* pod_realloc(T* prior, size_t oldSize, size_t newSize) {
|
||||
T* p2 = maybe_pod_realloc<T>(prior, oldSize, newSize);
|
||||
if (MOZ_UNLIKELY(!p2))
|
||||
p2 = onOutOfMemoryTyped<T>(AllocFunction::Realloc, newSize, prior);
|
||||
return p2;
|
||||
}
|
||||
|
||||
void free_(void* p) {
|
||||
js_free(p);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void) reportAllocOverflow() const;
|
||||
|
||||
bool checkSimulatedOOM() const {
|
||||
if (js::oom::ShouldFailWithOOM()) {
|
||||
js::ReportOutOfMemory(reinterpret_cast<ExclusiveContext*>(cx_));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsalloc_h */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,14 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef jsbytecode_h
|
||||
#define jsbytecode_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t jsbytecode;
|
||||
|
||||
#endif /* jsbytecode_h */
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef jsclist_h
|
||||
#define jsclist_h
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
/*
|
||||
** Circular linked list
|
||||
*/
|
||||
typedef struct JSCListStr {
|
||||
struct JSCListStr* next;
|
||||
struct JSCListStr* prev;
|
||||
} JSCList;
|
||||
|
||||
/*
|
||||
** Insert element "_e" into the list, before "_l".
|
||||
*/
|
||||
#define JS_INSERT_BEFORE(_e,_l) \
|
||||
JS_BEGIN_MACRO \
|
||||
(_e)->next = (_l); \
|
||||
(_e)->prev = (_l)->prev; \
|
||||
(_l)->prev->next = (_e); \
|
||||
(_l)->prev = (_e); \
|
||||
JS_END_MACRO
|
||||
|
||||
/*
|
||||
** Insert element "_e" into the list, after "_l".
|
||||
*/
|
||||
#define JS_INSERT_AFTER(_e,_l) \
|
||||
JS_BEGIN_MACRO \
|
||||
(_e)->next = (_l)->next; \
|
||||
(_e)->prev = (_l); \
|
||||
(_l)->next->prev = (_e); \
|
||||
(_l)->next = (_e); \
|
||||
JS_END_MACRO
|
||||
|
||||
/*
|
||||
** Return the element following element "_e"
|
||||
*/
|
||||
#define JS_NEXT_LINK(_e) \
|
||||
((_e)->next)
|
||||
/*
|
||||
** Return the element preceding element "_e"
|
||||
*/
|
||||
#define JS_PREV_LINK(_e) \
|
||||
((_e)->prev)
|
||||
|
||||
/*
|
||||
** Append an element "_e" to the end of the list "_l"
|
||||
*/
|
||||
#define JS_APPEND_LINK(_e,_l) JS_INSERT_BEFORE(_e,_l)
|
||||
|
||||
/*
|
||||
** Insert an element "_e" at the head of the list "_l"
|
||||
*/
|
||||
#define JS_INSERT_LINK(_e,_l) JS_INSERT_AFTER(_e,_l)
|
||||
|
||||
/* Return the head/tail of the list */
|
||||
#define JS_LIST_HEAD(_l) (_l)->next
|
||||
#define JS_LIST_TAIL(_l) (_l)->prev
|
||||
|
||||
/*
|
||||
** Remove the element "_e" from it's circular list.
|
||||
*/
|
||||
#define JS_REMOVE_LINK(_e) \
|
||||
JS_BEGIN_MACRO \
|
||||
(_e)->prev->next = (_e)->next; \
|
||||
(_e)->next->prev = (_e)->prev; \
|
||||
JS_END_MACRO
|
||||
|
||||
/*
|
||||
** Remove the element "_e" from it's circular list. Also initializes the
|
||||
** linkage.
|
||||
*/
|
||||
#define JS_REMOVE_AND_INIT_LINK(_e) \
|
||||
JS_BEGIN_MACRO \
|
||||
(_e)->prev->next = (_e)->next; \
|
||||
(_e)->next->prev = (_e)->prev; \
|
||||
(_e)->next = (_e); \
|
||||
(_e)->prev = (_e); \
|
||||
JS_END_MACRO
|
||||
|
||||
/*
|
||||
** Return non-zero if the given circular list "_l" is empty, zero if the
|
||||
** circular list is not empty
|
||||
*/
|
||||
#define JS_CLIST_IS_EMPTY(_l) \
|
||||
bool((_l)->next == (_l))
|
||||
|
||||
/*
|
||||
** Initialize a circular list
|
||||
*/
|
||||
#define JS_INIT_CLIST(_l) \
|
||||
JS_BEGIN_MACRO \
|
||||
(_l)->next = (_l); \
|
||||
(_l)->prev = (_l); \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_INIT_STATIC_CLIST(_l) \
|
||||
{(_l), (_l)}
|
||||
|
||||
#endif /* jsclist_h */
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef jscpucfg_h
|
||||
#define jscpucfg_h
|
||||
|
||||
#include "mozilla/EndianUtils.h"
|
||||
|
||||
#ifndef JS_STACK_GROWTH_DIRECTION
|
||||
# ifdef __hppa
|
||||
# define JS_STACK_GROWTH_DIRECTION (1)
|
||||
# else
|
||||
# define JS_STACK_GROWTH_DIRECTION (-1)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* jscpucfg_h */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,133 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; 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 perf_jsperf_h
|
||||
#define perf_jsperf_h
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
/*
|
||||
* JS::PerfMeasurement is a generic way to access detailed performance
|
||||
* measurement APIs provided by your operating system. The details of
|
||||
* exactly how this works and what can be measured are highly
|
||||
* system-specific, but this interface is (one hopes) implementable
|
||||
* on top of all of them.
|
||||
*
|
||||
* To use this API, create a PerfMeasurement object, passing its
|
||||
* constructor a bitmask indicating which events you are interested
|
||||
* in. Thereafter, Start() zeroes all counters and starts timing;
|
||||
* Stop() stops timing again; and the counters for the events you
|
||||
* requested are available as data values after calling Stop(). The
|
||||
* object may be reused for many measurements.
|
||||
*/
|
||||
class JS_FRIEND_API(PerfMeasurement)
|
||||
{
|
||||
protected:
|
||||
// Implementation-specific data, if any.
|
||||
void* impl;
|
||||
|
||||
public:
|
||||
/*
|
||||
* Events that may be measured. Taken directly from the list of
|
||||
* "generalized hardware performance event types" in the Linux
|
||||
* perf_event API, plus some of the "software events".
|
||||
*/
|
||||
enum EventMask {
|
||||
CPU_CYCLES = 0x00000001,
|
||||
INSTRUCTIONS = 0x00000002,
|
||||
CACHE_REFERENCES = 0x00000004,
|
||||
CACHE_MISSES = 0x00000008,
|
||||
BRANCH_INSTRUCTIONS = 0x00000010,
|
||||
BRANCH_MISSES = 0x00000020,
|
||||
BUS_CYCLES = 0x00000040,
|
||||
PAGE_FAULTS = 0x00000080,
|
||||
MAJOR_PAGE_FAULTS = 0x00000100,
|
||||
CONTEXT_SWITCHES = 0x00000200,
|
||||
CPU_MIGRATIONS = 0x00000400,
|
||||
|
||||
ALL = 0x000007ff,
|
||||
NUM_MEASURABLE_EVENTS = 11
|
||||
};
|
||||
|
||||
/*
|
||||
* Bitmask of events that will be measured when this object is
|
||||
* active (between Start() and Stop()). This may differ from the
|
||||
* bitmask passed to the constructor if the platform does not
|
||||
* support measuring all of the requested events.
|
||||
*/
|
||||
const EventMask eventsMeasured;
|
||||
|
||||
/*
|
||||
* Counters for each measurable event.
|
||||
* Immediately after one of these objects is created, all of the
|
||||
* counters for enabled events will be zero, and all of the
|
||||
* counters for disabled events will be uint64_t(-1).
|
||||
*/
|
||||
uint64_t cpu_cycles;
|
||||
uint64_t instructions;
|
||||
uint64_t cache_references;
|
||||
uint64_t cache_misses;
|
||||
uint64_t branch_instructions;
|
||||
uint64_t branch_misses;
|
||||
uint64_t bus_cycles;
|
||||
uint64_t page_faults;
|
||||
uint64_t major_page_faults;
|
||||
uint64_t context_switches;
|
||||
uint64_t cpu_migrations;
|
||||
|
||||
/*
|
||||
* Prepare to measure the indicated set of events. If not all of
|
||||
* the requested events can be measured on the current platform,
|
||||
* then the eventsMeasured bitmask will only include the subset of
|
||||
* |toMeasure| corresponding to the events that can be measured.
|
||||
*/
|
||||
explicit PerfMeasurement(EventMask toMeasure);
|
||||
|
||||
/* Done with this set of measurements, tear down OS-level state. */
|
||||
~PerfMeasurement();
|
||||
|
||||
/* Start a measurement cycle. */
|
||||
void start();
|
||||
|
||||
/*
|
||||
* End a measurement cycle, and for each enabled counter, add the
|
||||
* number of measured events of that type to the appropriate
|
||||
* visible variable.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/* Reset all enabled counters to zero. */
|
||||
void reset();
|
||||
|
||||
/*
|
||||
* True if this platform supports measuring _something_, i.e. it's
|
||||
* not using the stub implementation.
|
||||
*/
|
||||
static bool canMeasureSomething();
|
||||
};
|
||||
|
||||
/* Inject a Javascript wrapper around the above C++ class into the
|
||||
* Javascript object passed as an argument (this will normally be a
|
||||
* global object). The JS-visible API is identical to the C++ API.
|
||||
*/
|
||||
extern JS_FRIEND_API(JSObject*)
|
||||
RegisterPerfMeasurement(JSContext* cx, JS::HandleObject global);
|
||||
|
||||
/*
|
||||
* Given a Value which contains an instance of the aforementioned
|
||||
* wrapper class, extract the C++ object. Returns nullptr if the
|
||||
* Value is not an instance of the wrapper.
|
||||
*/
|
||||
extern JS_FRIEND_API(PerfMeasurement*)
|
||||
ExtractPerfMeasurement(const Value& wrapper);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* perf_jsperf_h */
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef jsprf_h
|
||||
#define jsprf_h
|
||||
|
||||
/*
|
||||
** API for PR printf like routines. Supports the following formats
|
||||
** %d - decimal
|
||||
** %u - unsigned decimal
|
||||
** %x - unsigned hex
|
||||
** %X - unsigned uppercase hex
|
||||
** %o - unsigned octal
|
||||
** %hd, %hu, %hx, %hX, %ho - "short" versions of above
|
||||
** %ld, %lu, %lx, %lX, %lo - "long" versions of above
|
||||
** %lld, %llu, %llx, %llX, %llo - "long long" versions of above
|
||||
** %zd, %zo, %zu, %zx, %zX - size_t versions of above
|
||||
** %Id, %Io, %Iu, %Ix, %IX - size_t versions of above (for Windows compat)
|
||||
** You should use PRI*SIZE macros instead
|
||||
** %s - string
|
||||
** %c - character
|
||||
** %p - pointer (deals with machine dependent pointer size)
|
||||
** %f - float
|
||||
** %g - float
|
||||
*/
|
||||
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/SizePrintfMacros.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
/*
|
||||
** sprintf into a malloc'd buffer. Return a pointer to the malloc'd
|
||||
** buffer on success, nullptr on failure. Call "JS_smprintf_free" to release
|
||||
** the memory returned.
|
||||
*/
|
||||
extern JS_PUBLIC_API(char*) JS_smprintf(const char* fmt, ...)
|
||||
MOZ_FORMAT_PRINTF(1, 2);
|
||||
|
||||
/*
|
||||
** Free the memory allocated, for the caller, by JS_smprintf
|
||||
*/
|
||||
extern JS_PUBLIC_API(void) JS_smprintf_free(char* mem);
|
||||
|
||||
/*
|
||||
** "append" sprintf into a malloc'd buffer. "last" is the last value of
|
||||
** the malloc'd buffer. sprintf will append data to the end of last,
|
||||
** growing it as necessary using realloc. If last is nullptr, JS_sprintf_append
|
||||
** will allocate the initial string. The return value is the new value of
|
||||
** last for subsequent calls, or nullptr if there is a malloc failure.
|
||||
*/
|
||||
extern JS_PUBLIC_API(char*) JS_sprintf_append(char* last, const char* fmt, ...)
|
||||
MOZ_FORMAT_PRINTF(2, 3);
|
||||
|
||||
/*
|
||||
** va_list forms of the above.
|
||||
*/
|
||||
extern JS_PUBLIC_API(char*) JS_vsmprintf(const char* fmt, va_list ap);
|
||||
extern JS_PUBLIC_API(char*) JS_vsprintf_append(char* last, const char* fmt, va_list ap);
|
||||
|
||||
#endif /* jsprf_h */
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef jsprototypes_h
|
||||
#define jsprototypes_h
|
||||
|
||||
/* A higher-order macro for enumerating all JSProtoKey values. */
|
||||
/*
|
||||
* Consumers define macros as follows:
|
||||
* macro(name, code, init, clasp)
|
||||
* name: The canonical name of the class.
|
||||
* code: The enumerator code. There are part of the XDR API, and must not change.
|
||||
* init: Initialization function. These are |extern "C";|, and clients should use
|
||||
* |extern "C" {}| as appropriate when using this macro.
|
||||
* clasp: The JSClass for this object, or "dummy" if it doesn't exist.
|
||||
*
|
||||
*
|
||||
* Consumers wishing to iterate over all the JSProtoKey values, can use
|
||||
* JS_FOR_EACH_PROTOTYPE. However, there are certain values that don't correspond
|
||||
* to real constructors, like Null or constructors that are disabled via
|
||||
* preprocessor directives. We still need to include these in the JSProtoKey list
|
||||
* in order to maintain binary XDR compatibility, but we need to provide a tool
|
||||
* to handle them differently. JS_FOR_PROTOTYPES fills this niche.
|
||||
*
|
||||
* Consumers pass two macros to JS_FOR_PROTOTYPES - |real| and |imaginary|. The
|
||||
* former is invoked for entries that have real client-exposed constructors, and
|
||||
* the latter is called for the rest. Consumers that don't care about this
|
||||
* distinction can simply pass the same macro to both, which is exactly what
|
||||
* JS_FOR_EACH_PROTOTYPE does.
|
||||
*/
|
||||
|
||||
#define CLASP(name) (&name##Class)
|
||||
#define OCLASP(name) (&name##Object::class_)
|
||||
#define TYPED_ARRAY_CLASP(type) (&TypedArrayObject::classes[Scalar::type])
|
||||
#define ERROR_CLASP(type) (&ErrorObject::classes[type])
|
||||
|
||||
#ifdef EXPOSE_INTL_API
|
||||
#define IF_INTL(real,imaginary) real
|
||||
#else
|
||||
#define IF_INTL(real,imaginary) imaginary
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BINARYDATA
|
||||
#define IF_BDATA(real,imaginary) real
|
||||
#else
|
||||
#define IF_BDATA(real,imaginary) imaginary
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SIMD
|
||||
# define IF_SIMD(real,imaginary) real
|
||||
#else
|
||||
# define IF_SIMD(real,imaginary) imaginary
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SHARED_ARRAY_BUFFER
|
||||
#define IF_SAB(real,imaginary) real
|
||||
#else
|
||||
#define IF_SAB(real,imaginary) imaginary
|
||||
#endif
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
#define IF_PROMISE(real,imaginary) real
|
||||
#else
|
||||
#define IF_PROMISE(real,imaginary) imaginary
|
||||
#endif
|
||||
|
||||
#define JS_FOR_PROTOTYPES(real,imaginary) \
|
||||
imaginary(Null, 0, InitNullClass, dummy) \
|
||||
real(Object, 1, InitViaClassSpec, OCLASP(Plain)) \
|
||||
real(Function, 2, InitViaClassSpec, &JSFunction::class_) \
|
||||
real(Array, 3, InitViaClassSpec, OCLASP(Array)) \
|
||||
real(Boolean, 4, InitBooleanClass, OCLASP(Boolean)) \
|
||||
real(JSON, 5, InitJSONClass, CLASP(JSON)) \
|
||||
real(Date, 6, InitViaClassSpec, OCLASP(Date)) \
|
||||
real(Math, 7, InitMathClass, CLASP(Math)) \
|
||||
real(Number, 8, InitNumberClass, OCLASP(Number)) \
|
||||
real(String, 9, InitStringClass, OCLASP(String)) \
|
||||
real(RegExp, 10, InitViaClassSpec, OCLASP(RegExp)) \
|
||||
real(Error, 11, InitViaClassSpec, ERROR_CLASP(JSEXN_ERR)) \
|
||||
real(InternalError, 12, InitViaClassSpec, ERROR_CLASP(JSEXN_INTERNALERR)) \
|
||||
real(EvalError, 13, InitViaClassSpec, ERROR_CLASP(JSEXN_EVALERR)) \
|
||||
real(RangeError, 14, InitViaClassSpec, ERROR_CLASP(JSEXN_RANGEERR)) \
|
||||
real(ReferenceError, 15, InitViaClassSpec, ERROR_CLASP(JSEXN_REFERENCEERR)) \
|
||||
real(SyntaxError, 16, InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR)) \
|
||||
real(TypeError, 17, InitViaClassSpec, ERROR_CLASP(JSEXN_TYPEERR)) \
|
||||
real(URIError, 18, InitViaClassSpec, ERROR_CLASP(JSEXN_URIERR)) \
|
||||
real(DebuggeeWouldRun, 19, InitViaClassSpec, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN)) \
|
||||
real(CompileError, 20, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMCOMPILEERROR)) \
|
||||
real(RuntimeError, 21, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR)) \
|
||||
real(Iterator, 22, InitLegacyIteratorClass,OCLASP(PropertyIterator)) \
|
||||
real(StopIteration, 23, InitStopIterationClass, OCLASP(StopIteration)) \
|
||||
real(ArrayBuffer, 24, InitViaClassSpec, OCLASP(ArrayBuffer)) \
|
||||
real(Int8Array, 25, InitViaClassSpec, TYPED_ARRAY_CLASP(Int8)) \
|
||||
real(Uint8Array, 26, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8)) \
|
||||
real(Int16Array, 27, InitViaClassSpec, TYPED_ARRAY_CLASP(Int16)) \
|
||||
real(Uint16Array, 28, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint16)) \
|
||||
real(Int32Array, 29, InitViaClassSpec, TYPED_ARRAY_CLASP(Int32)) \
|
||||
real(Uint32Array, 30, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint32)) \
|
||||
real(Float32Array, 31, InitViaClassSpec, TYPED_ARRAY_CLASP(Float32)) \
|
||||
real(Float64Array, 32, InitViaClassSpec, TYPED_ARRAY_CLASP(Float64)) \
|
||||
real(Uint8ClampedArray, 33, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8Clamped)) \
|
||||
real(Proxy, 34, InitProxyClass, js::ProxyClassPtr) \
|
||||
real(WeakMap, 35, InitWeakMapClass, OCLASP(WeakMap)) \
|
||||
real(Map, 36, InitMapClass, OCLASP(Map)) \
|
||||
real(Set, 37, InitSetClass, OCLASP(Set)) \
|
||||
real(DataView, 38, InitDataViewClass, OCLASP(DataView)) \
|
||||
real(Symbol, 39, InitSymbolClass, OCLASP(Symbol)) \
|
||||
IF_SAB(real,imaginary)(SharedArrayBuffer, 40, InitViaClassSpec, OCLASP(SharedArrayBuffer)) \
|
||||
IF_INTL(real,imaginary) (Intl, 41, InitIntlClass, CLASP(Intl)) \
|
||||
IF_BDATA(real,imaginary)(TypedObject, 42, InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \
|
||||
real(Reflect, 43, InitReflect, nullptr) \
|
||||
IF_SIMD(real,imaginary)(SIMD, 44, InitSimdClass, OCLASP(Simd)) \
|
||||
real(WeakSet, 45, InitWeakSetClass, OCLASP(WeakSet)) \
|
||||
real(TypedArray, 46, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
|
||||
IF_SAB(real,imaginary)(Atomics, 47, InitAtomicsClass, OCLASP(Atomics)) \
|
||||
real(SavedFrame, 48, InitViaClassSpec, &js::SavedFrame::class_) \
|
||||
real(WebAssembly, 49, InitWebAssemblyClass, CLASP(WebAssembly)) \
|
||||
imaginary(WasmModule, 50, dummy, dummy) \
|
||||
imaginary(WasmInstance, 51, dummy, dummy) \
|
||||
imaginary(WasmMemory, 52, dummy, dummy) \
|
||||
imaginary(WasmTable, 53, dummy, dummy) \
|
||||
IF_PROMISE(real,imaginary)(Promise, 54, InitViaClassSpec, OCLASP(Promise)) \
|
||||
|
||||
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
|
||||
|
||||
#endif /* jsprototypes_h */
|
||||
|
|
@ -1,476 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef jspubtd_h
|
||||
#define jspubtd_h
|
||||
|
||||
/*
|
||||
* JS public API typedefs.
|
||||
*/
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
||||
#include "jsprototypes.h"
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/TraceKind.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
#if defined(JS_GC_ZEAL) || defined(DEBUG)
|
||||
# define JSGC_HASH_TABLE_CHECKS
|
||||
#endif
|
||||
|
||||
namespace JS {
|
||||
|
||||
class AutoIdVector;
|
||||
class CallArgs;
|
||||
|
||||
template <typename T>
|
||||
class Rooted;
|
||||
|
||||
class JS_FRIEND_API(CompileOptions);
|
||||
class JS_FRIEND_API(ReadOnlyCompileOptions);
|
||||
class JS_FRIEND_API(OwningCompileOptions);
|
||||
class JS_FRIEND_API(TransitiveCompileOptions);
|
||||
class JS_PUBLIC_API(CompartmentOptions);
|
||||
|
||||
struct RootingContext;
|
||||
class Value;
|
||||
struct Zone;
|
||||
|
||||
namespace shadow {
|
||||
struct Runtime;
|
||||
} // namespace shadow
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
class RootLists;
|
||||
} // namespace js
|
||||
|
||||
/*
|
||||
* Run-time version enumeration. For compile-time version checking, please use
|
||||
* the JS_HAS_* macros in jsversion.h, or use MOZJS_MAJOR_VERSION,
|
||||
* MOZJS_MINOR_VERSION, MOZJS_PATCH_VERSION, and MOZJS_ALPHA definitions.
|
||||
*/
|
||||
enum JSVersion {
|
||||
JSVERSION_ECMA_3 = 148,
|
||||
JSVERSION_1_6 = 160,
|
||||
JSVERSION_1_7 = 170,
|
||||
JSVERSION_1_8 = 180,
|
||||
JSVERSION_ECMA_5 = 185,
|
||||
JSVERSION_DEFAULT = 0,
|
||||
JSVERSION_UNKNOWN = -1,
|
||||
JSVERSION_LATEST = JSVERSION_ECMA_5
|
||||
};
|
||||
|
||||
/* Result of typeof operator enumeration. */
|
||||
enum JSType {
|
||||
JSTYPE_VOID, /* undefined */
|
||||
JSTYPE_OBJECT, /* object */
|
||||
JSTYPE_FUNCTION, /* function */
|
||||
JSTYPE_STRING, /* string */
|
||||
JSTYPE_NUMBER, /* number */
|
||||
JSTYPE_BOOLEAN, /* boolean */
|
||||
JSTYPE_NULL, /* null */
|
||||
JSTYPE_SYMBOL, /* symbol */
|
||||
JSTYPE_LIMIT
|
||||
};
|
||||
|
||||
/* Dense index into cached prototypes and class atoms for standard objects. */
|
||||
enum JSProtoKey {
|
||||
#define PROTOKEY_AND_INITIALIZER(name,code,init,clasp) JSProto_##name = code,
|
||||
JS_FOR_EACH_PROTOTYPE(PROTOKEY_AND_INITIALIZER)
|
||||
#undef PROTOKEY_AND_INITIALIZER
|
||||
JSProto_LIMIT
|
||||
};
|
||||
|
||||
/* Struct forward declarations. */
|
||||
struct JSClass;
|
||||
struct JSCompartment;
|
||||
struct JSCrossCompartmentCall;
|
||||
class JSErrorReport;
|
||||
struct JSExceptionState;
|
||||
struct JSFunctionSpec;
|
||||
struct JSLocaleCallbacks;
|
||||
struct JSObjectMap;
|
||||
struct JSPrincipals;
|
||||
struct JSPropertyName;
|
||||
struct JSPropertySpec;
|
||||
struct JSRuntime;
|
||||
struct JSSecurityCallbacks;
|
||||
struct JSStructuredCloneCallbacks;
|
||||
struct JSStructuredCloneReader;
|
||||
struct JSStructuredCloneWriter;
|
||||
class JS_PUBLIC_API(JSTracer);
|
||||
|
||||
class JSFlatString;
|
||||
|
||||
typedef bool (*JSInitCallback)(void);
|
||||
|
||||
template<typename T> struct JSConstScalarSpec;
|
||||
typedef JSConstScalarSpec<double> JSConstDoubleSpec;
|
||||
typedef JSConstScalarSpec<int32_t> JSConstIntegerSpec;
|
||||
|
||||
/*
|
||||
* Generic trace operation that calls JS::TraceEdge on each traceable thing's
|
||||
* location reachable from data.
|
||||
*/
|
||||
typedef void
|
||||
(* JSTraceDataOp)(JSTracer* trc, void* data);
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
class AutoTraceSession;
|
||||
class StoreBuffer;
|
||||
} // namespace gc
|
||||
|
||||
// Whether the current thread is permitted access to any part of the specified
|
||||
// runtime or zone.
|
||||
JS_FRIEND_API(bool)
|
||||
CurrentThreadCanAccessRuntime(const JSRuntime* rt);
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FRIEND_API(bool)
|
||||
CurrentThreadIsPerformingGC();
|
||||
#endif
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
class JS_PUBLIC_API(AutoEnterCycleCollection);
|
||||
class JS_PUBLIC_API(AutoAssertOnBarrier);
|
||||
struct JS_PUBLIC_API(PropertyDescriptor);
|
||||
|
||||
typedef void (*OffThreadCompileCallback)(void* token, void* callbackData);
|
||||
|
||||
enum class HeapState {
|
||||
Idle, // doing nothing with the GC heap
|
||||
Tracing, // tracing the GC heap without collecting, e.g. IterateCompartments()
|
||||
MajorCollecting, // doing a GC of the major heap
|
||||
MinorCollecting, // doing a GC of the minor heap (nursery)
|
||||
CycleCollecting // in the "Unlink" phase of cycle collection
|
||||
};
|
||||
|
||||
namespace shadow {
|
||||
|
||||
struct Runtime
|
||||
{
|
||||
private:
|
||||
JS::HeapState heapState_;
|
||||
|
||||
protected:
|
||||
void setHeapState(JS::HeapState newState) {
|
||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(asRuntime()));
|
||||
MOZ_ASSERT(heapState_ != newState);
|
||||
heapState_ = newState;
|
||||
}
|
||||
|
||||
JS::HeapState heapState() const {
|
||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(asRuntime()) ||
|
||||
js::CurrentThreadIsPerformingGC());
|
||||
return heapState_;
|
||||
}
|
||||
|
||||
// In some cases, invoking GC barriers (incremental or otherwise) will break
|
||||
// things. These barriers assert if this flag is set.
|
||||
bool allowGCBarriers_;
|
||||
friend class JS::AutoAssertOnBarrier;
|
||||
|
||||
js::gc::StoreBuffer* gcStoreBufferPtr_;
|
||||
|
||||
// The gray bits can become invalid if UnmarkGray overflows the stack. A
|
||||
// full GC will reset this bit, since it fills in all the gray bits.
|
||||
bool gcGrayBitsValid_;
|
||||
|
||||
public:
|
||||
Runtime()
|
||||
: heapState_(JS::HeapState::Idle)
|
||||
, allowGCBarriers_(true)
|
||||
, gcStoreBufferPtr_(nullptr)
|
||||
, gcGrayBitsValid_(false)
|
||||
{}
|
||||
|
||||
bool isHeapBusy() const { return heapState() != JS::HeapState::Idle; }
|
||||
bool isHeapTracing() const { return heapState() == JS::HeapState::Tracing; }
|
||||
bool isHeapMajorCollecting() const { return heapState() == JS::HeapState::MajorCollecting; }
|
||||
bool isHeapMinorCollecting() const { return heapState() == JS::HeapState::MinorCollecting; }
|
||||
bool isHeapCollecting() const { return isHeapMinorCollecting() || isHeapMajorCollecting(); }
|
||||
bool isCycleCollecting() const {
|
||||
return heapState() == JS::HeapState::CycleCollecting;
|
||||
}
|
||||
|
||||
bool allowGCBarriers() const { return allowGCBarriers_; }
|
||||
|
||||
js::gc::StoreBuffer* gcStoreBufferPtr() { return gcStoreBufferPtr_; }
|
||||
|
||||
bool areGCGrayBitsValid() const { return gcGrayBitsValid_; }
|
||||
void setGCGrayBitsValid(bool valid) { gcGrayBitsValid_ = valid; }
|
||||
|
||||
const JSRuntime* asRuntime() const {
|
||||
return reinterpret_cast<const JSRuntime*>(this);
|
||||
}
|
||||
|
||||
static JS::shadow::Runtime* asShadowRuntime(JSRuntime* rt) {
|
||||
return reinterpret_cast<JS::shadow::Runtime*>(rt);
|
||||
}
|
||||
|
||||
protected:
|
||||
void setGCStoreBufferPtr(js::gc::StoreBuffer* storeBuffer) {
|
||||
gcStoreBufferPtr_ = storeBuffer;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace shadow */
|
||||
|
||||
// Decorates the Unlinking phase of CycleCollection so that accidental use
|
||||
// of barriered accessors results in assertions instead of leaks.
|
||||
class MOZ_STACK_CLASS JS_PUBLIC_API(AutoEnterCycleCollection)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JSRuntime* runtime;
|
||||
|
||||
public:
|
||||
explicit AutoEnterCycleCollection(JSContext* cx);
|
||||
~AutoEnterCycleCollection();
|
||||
#else
|
||||
public:
|
||||
explicit AutoEnterCycleCollection(JSContext* cx) {}
|
||||
~AutoEnterCycleCollection() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
class JS_PUBLIC_API(AutoGCRooter)
|
||||
{
|
||||
public:
|
||||
AutoGCRooter(JSContext* cx, ptrdiff_t tag);
|
||||
AutoGCRooter(JS::RootingContext* cx, ptrdiff_t tag);
|
||||
|
||||
~AutoGCRooter() {
|
||||
MOZ_ASSERT(this == *stackTop);
|
||||
*stackTop = down;
|
||||
}
|
||||
|
||||
/* Implemented in gc/RootMarking.cpp. */
|
||||
inline void trace(JSTracer* trc);
|
||||
static void traceAll(JSTracer* trc);
|
||||
static void traceAllWrappers(JSTracer* trc);
|
||||
|
||||
protected:
|
||||
AutoGCRooter * const down;
|
||||
|
||||
/*
|
||||
* Discriminates actual subclass of this being used. If non-negative, the
|
||||
* subclass roots an array of values of the length stored in this field.
|
||||
* If negative, meaning is indicated by the corresponding value in the enum
|
||||
* below. Any other negative value indicates some deeper problem such as
|
||||
* memory corruption.
|
||||
*/
|
||||
ptrdiff_t tag_;
|
||||
|
||||
enum {
|
||||
VALARRAY = -2, /* js::AutoValueArray */
|
||||
PARSER = -3, /* js::frontend::Parser */
|
||||
VALVECTOR = -10, /* js::AutoValueVector */
|
||||
IDVECTOR = -11, /* js::AutoIdVector */
|
||||
OBJVECTOR = -14, /* js::AutoObjectVector */
|
||||
IONMASM = -19, /* js::jit::MacroAssembler */
|
||||
WRAPVECTOR = -20, /* js::AutoWrapperVector */
|
||||
WRAPPER = -21, /* js::AutoWrapperRooter */
|
||||
CUSTOM = -26 /* js::CustomAutoRooter */
|
||||
};
|
||||
|
||||
static ptrdiff_t GetTag(const Value& value) { return VALVECTOR; }
|
||||
static ptrdiff_t GetTag(const jsid& id) { return IDVECTOR; }
|
||||
static ptrdiff_t GetTag(JSObject* obj) { return OBJVECTOR; }
|
||||
|
||||
private:
|
||||
AutoGCRooter ** const stackTop;
|
||||
|
||||
/* No copy or assignment semantics. */
|
||||
AutoGCRooter(AutoGCRooter& ida) = delete;
|
||||
void operator=(AutoGCRooter& ida) = delete;
|
||||
};
|
||||
|
||||
// Our instantiations of Rooted<void*> and PersistentRooted<void*> require an
|
||||
// instantiation of MapTypeToRootKind.
|
||||
template <>
|
||||
struct MapTypeToRootKind<void*> {
|
||||
static const RootKind kind = RootKind::Traceable;
|
||||
};
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
|
||||
class ExclusiveContext;
|
||||
|
||||
/*
|
||||
* This list enumerates the different types of conceptual stacks we have in
|
||||
* SpiderMonkey. In reality, they all share the C stack, but we allow different
|
||||
* stack limits depending on the type of code running.
|
||||
*/
|
||||
enum StackKind
|
||||
{
|
||||
StackForSystemCode, // C++, such as the GC, running on behalf of the VM.
|
||||
StackForTrustedScript, // Script running with trusted principals.
|
||||
StackForUntrustedScript, // Script running with untrusted principals.
|
||||
StackKindCount
|
||||
};
|
||||
|
||||
using RootedListHeads = mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit,
|
||||
JS::Rooted<void*>*>;
|
||||
|
||||
// Abstracts JS rooting mechanisms so they can be shared between the JSContext
|
||||
// and JSRuntime.
|
||||
class RootLists
|
||||
{
|
||||
// Stack GC roots for Rooted GC heap pointers.
|
||||
RootedListHeads stackRoots_;
|
||||
template <typename T> friend class JS::Rooted;
|
||||
|
||||
// Stack GC roots for AutoFooRooter classes.
|
||||
JS::AutoGCRooter* autoGCRooters_;
|
||||
friend class JS::AutoGCRooter;
|
||||
|
||||
// Heap GC roots for PersistentRooted pointers.
|
||||
mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit,
|
||||
mozilla::LinkedList<JS::PersistentRooted<void*>>> heapRoots_;
|
||||
template <typename T> friend class JS::PersistentRooted;
|
||||
|
||||
public:
|
||||
RootLists() : autoGCRooters_(nullptr) {
|
||||
for (auto& stackRootPtr : stackRoots_)
|
||||
stackRootPtr = nullptr;
|
||||
}
|
||||
|
||||
~RootLists() {
|
||||
// The semantics of PersistentRooted containing pointers and tagged
|
||||
// pointers are somewhat different from those of PersistentRooted
|
||||
// containing a structure with a trace method. PersistentRooted
|
||||
// containing pointers are allowed to outlive the owning RootLists,
|
||||
// whereas those containing a traceable structure are not.
|
||||
//
|
||||
// The purpose of this feature is to support lazy initialization of
|
||||
// global references for the several places in Gecko that do not have
|
||||
// access to a tighter context, but that still need to refer to GC
|
||||
// pointers. For such pointers, FinishPersistentRootedChains ensures
|
||||
// that the contained references are nulled out when the owning
|
||||
// RootLists dies to prevent UAF errors.
|
||||
//
|
||||
// However, for RootKind::Traceable, we do not know the concrete type
|
||||
// of the held thing, so we simply cannot do this without accruing
|
||||
// extra overhead and complexity for all users for a case that is
|
||||
// unlikely to ever be used in practice. For this reason, the following
|
||||
// assertion disallows usage of PersistentRooted<Traceable> that
|
||||
// outlives the RootLists.
|
||||
MOZ_ASSERT(heapRoots_[JS::RootKind::Traceable].isEmpty());
|
||||
}
|
||||
|
||||
void traceStackRoots(JSTracer* trc);
|
||||
void checkNoGCRooters();
|
||||
|
||||
void tracePersistentRoots(JSTracer* trc);
|
||||
void finishPersistentRoots();
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
/*
|
||||
* JS::RootingContext is a base class of ContextFriendFields and JSContext.
|
||||
* This class can be used to let code construct a Rooted<> or PersistentRooted<>
|
||||
* instance, without giving it full access to the JSContext.
|
||||
*/
|
||||
struct RootingContext
|
||||
{
|
||||
js::RootLists roots;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Whether the derived class is a JSContext or an ExclusiveContext.
|
||||
bool isJSContext;
|
||||
#endif
|
||||
|
||||
explicit RootingContext(bool isJSContextArg)
|
||||
#ifdef DEBUG
|
||||
: isJSContext(isJSContextArg)
|
||||
#endif
|
||||
{}
|
||||
|
||||
static RootingContext* get(JSContext* cx) {
|
||||
return reinterpret_cast<RootingContext*>(cx);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
struct ContextFriendFields : public JS::RootingContext
|
||||
{
|
||||
protected:
|
||||
/* The current compartment. */
|
||||
JSCompartment* compartment_;
|
||||
|
||||
/* The current zone. */
|
||||
JS::Zone* zone_;
|
||||
|
||||
public:
|
||||
/* Limit pointer for checking native stack consumption. */
|
||||
uintptr_t nativeStackLimit[js::StackKindCount];
|
||||
|
||||
explicit ContextFriendFields(bool isJSContext);
|
||||
|
||||
static const ContextFriendFields* get(const JSContext* cx) {
|
||||
return reinterpret_cast<const ContextFriendFields*>(cx);
|
||||
}
|
||||
|
||||
static ContextFriendFields* get(JSContext* cx) {
|
||||
return reinterpret_cast<ContextFriendFields*>(cx);
|
||||
}
|
||||
|
||||
friend JSCompartment* GetContextCompartment(const JSContext* cx);
|
||||
friend JS::Zone* GetContextZone(const JSContext* cx);
|
||||
template <typename T> friend class JS::Rooted;
|
||||
};
|
||||
|
||||
/*
|
||||
* Inlinable accessors for JSContext.
|
||||
*
|
||||
* - These must not be available on the more restricted superclasses of
|
||||
* JSContext, so we can't simply define them on ContextFriendFields.
|
||||
*
|
||||
* - They're perfectly ordinary JSContext functionality, so ought to be
|
||||
* usable without resorting to jsfriendapi.h, and when JSContext is an
|
||||
* incomplete type.
|
||||
*/
|
||||
inline JSCompartment*
|
||||
GetContextCompartment(const JSContext* cx)
|
||||
{
|
||||
return ContextFriendFields::get(cx)->compartment_;
|
||||
}
|
||||
|
||||
inline JS::Zone*
|
||||
GetContextZone(const JSContext* cx)
|
||||
{
|
||||
return ContextFriendFields::get(cx)->zone_;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
MOZ_BEGIN_EXTERN_C
|
||||
|
||||
// Defined in NSPR prio.h.
|
||||
typedef struct PRFileDesc PRFileDesc;
|
||||
|
||||
MOZ_END_EXTERN_C
|
||||
|
||||
#endif /* jspubtd_h */
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/*
|
||||
** File: jstypes.h
|
||||
** Description: Definitions of NSPR's basic types
|
||||
**
|
||||
** Prototypes and macros used to make up for deficiencies in ANSI environments
|
||||
** that we have found.
|
||||
**
|
||||
** Since we do not wrap <stdlib.h> and all the other standard headers, authors
|
||||
** of portable code will not know in general that they need these definitions.
|
||||
** Instead of requiring these authors to find the dependent uses in their code
|
||||
** and take the following steps only in those C files, we take steps once here
|
||||
** for all C files.
|
||||
**/
|
||||
|
||||
#ifndef jstypes_h
|
||||
#define jstypes_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
// jstypes.h is (or should be!) included by every file in SpiderMonkey.
|
||||
// js-config.h and jsversion.h also should be included by every file.
|
||||
// So include them here.
|
||||
// XXX: including them in js/RequiredDefines.h should be a better option, since
|
||||
// that is by definition the header file that should be included in all
|
||||
// SpiderMonkey code. However, Gecko doesn't do this! See bug 909576.
|
||||
#include "js-config.h"
|
||||
#include "jsversion.h"
|
||||
|
||||
/***********************************************************************
|
||||
** MACROS: JS_EXTERN_API
|
||||
** JS_EXPORT_API
|
||||
** DESCRIPTION:
|
||||
** These are only for externally visible routines and globals. For
|
||||
** internal routines, just use "extern" for type checking and that
|
||||
** will not export internal cross-file or forward-declared symbols.
|
||||
** Define a macro for declaring procedures return types. We use this to
|
||||
** deal with windoze specific type hackery for DLL definitions. Use
|
||||
** JS_EXTERN_API when the prototype for the method is declared. Use
|
||||
** JS_EXPORT_API for the implementation of the method.
|
||||
**
|
||||
** Example:
|
||||
** in dowhim.h
|
||||
** JS_EXTERN_API( void ) DoWhatIMean( void );
|
||||
** in dowhim.c
|
||||
** JS_EXPORT_API( void ) DoWhatIMean( void ) { return; }
|
||||
**
|
||||
**
|
||||
***********************************************************************/
|
||||
|
||||
#define JS_EXTERN_API(type) extern MOZ_EXPORT type
|
||||
#define JS_EXPORT_API(type) MOZ_EXPORT type
|
||||
#define JS_EXPORT_DATA(type) MOZ_EXPORT type
|
||||
#define JS_IMPORT_API(type) MOZ_IMPORT_API type
|
||||
#define JS_IMPORT_DATA(type) MOZ_IMPORT_DATA type
|
||||
|
||||
/*
|
||||
* The linkage of JS API functions differs depending on whether the file is
|
||||
* used within the JS library or not. Any source file within the JS
|
||||
* interpreter should define EXPORT_JS_API whereas any client of the library
|
||||
* should not. STATIC_JS_API is used to build JS as a static library.
|
||||
*/
|
||||
#if defined(STATIC_JS_API)
|
||||
# define JS_PUBLIC_API(t) t
|
||||
# define JS_PUBLIC_DATA(t) t
|
||||
# define JS_FRIEND_API(t) t
|
||||
# define JS_FRIEND_DATA(t) t
|
||||
#elif defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API)
|
||||
# define JS_PUBLIC_API(t) MOZ_EXPORT t
|
||||
# define JS_PUBLIC_DATA(t) MOZ_EXPORT t
|
||||
# define JS_FRIEND_API(t) MOZ_EXPORT t
|
||||
# define JS_FRIEND_DATA(t) MOZ_EXPORT t
|
||||
#else
|
||||
# define JS_PUBLIC_API(t) MOZ_IMPORT_API t
|
||||
# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA t
|
||||
# define JS_FRIEND_API(t) MOZ_IMPORT_API t
|
||||
# define JS_FRIEND_DATA(t) MOZ_IMPORT_DATA t
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_IX86)
|
||||
#define JS_FASTCALL __fastcall
|
||||
#elif defined(__GNUC__) && defined(__i386__)
|
||||
#define JS_FASTCALL __attribute__((fastcall))
|
||||
#else
|
||||
#define JS_FASTCALL
|
||||
#define JS_NO_FASTCALL
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
** MACROS: JS_BEGIN_MACRO
|
||||
** JS_END_MACRO
|
||||
** DESCRIPTION:
|
||||
** Macro body brackets so that macros with compound statement definitions
|
||||
** behave syntactically more like functions when called.
|
||||
***********************************************************************/
|
||||
#define JS_BEGIN_MACRO do {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define JS_END_MACRO \
|
||||
} __pragma(warning(push)) __pragma(warning(disable:4127)) \
|
||||
while (0) __pragma(warning(pop))
|
||||
#else
|
||||
# define JS_END_MACRO } while (0)
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
** MACROS: JS_BIT
|
||||
** JS_BITMASK
|
||||
** DESCRIPTION:
|
||||
** Bit masking macros. XXX n must be <= 31 to be portable
|
||||
***********************************************************************/
|
||||
#define JS_BIT(n) ((uint32_t)1 << (n))
|
||||
#define JS_BITMASK(n) (JS_BIT(n) - 1)
|
||||
|
||||
/***********************************************************************
|
||||
** MACROS: JS_HOWMANY
|
||||
** JS_ROUNDUP
|
||||
** DESCRIPTION:
|
||||
** Commonly used macros for operations on compatible types.
|
||||
***********************************************************************/
|
||||
#define JS_HOWMANY(x,y) (((x)+(y)-1)/(y))
|
||||
#define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y))
|
||||
|
||||
#include "jscpucfg.h"
|
||||
|
||||
/*
|
||||
* Define JS_64BIT iff we are building in an environment with 64-bit
|
||||
* addresses.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
# if defined(_M_X64) || defined(_M_AMD64)
|
||||
# define JS_64BIT
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
/* Additional GCC defines are when running on Solaris, AIX, and HPUX */
|
||||
# if defined(__x86_64__) || defined(__sparcv9) || \
|
||||
defined(__64BIT__) || defined(__LP64__)
|
||||
# define JS_64BIT
|
||||
# endif
|
||||
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Sun Studio C/C++ */
|
||||
# if defined(__x86_64) || defined(__sparcv9)
|
||||
# define JS_64BIT
|
||||
# endif
|
||||
#elif defined(__xlc__) || defined(__xlC__) /* IBM XL C/C++ */
|
||||
# if defined(__64BIT__)
|
||||
# define JS_64BIT
|
||||
# endif
|
||||
#elif defined(__HP_cc) || defined(__HP_aCC) /* HP-UX cc/aCC */
|
||||
# if defined(__LP64__)
|
||||
# define JS_64BIT
|
||||
# endif
|
||||
#else
|
||||
# error "Implement me"
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
** MACROS: JS_ARRAY_LENGTH
|
||||
** JS_ARRAY_END
|
||||
** DESCRIPTION:
|
||||
** Macros to get the number of elements and the pointer to one past the
|
||||
** last element of a C array. Use them like this:
|
||||
**
|
||||
** char16_t buf[10];
|
||||
** JSString* str;
|
||||
** ...
|
||||
** for (char16_t* s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...;
|
||||
** ...
|
||||
** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf));
|
||||
** ...
|
||||
**
|
||||
***********************************************************************/
|
||||
|
||||
#define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0])
|
||||
#define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array))
|
||||
|
||||
#define JS_BITS_PER_BYTE 8
|
||||
#define JS_BITS_PER_BYTE_LOG2 3
|
||||
|
||||
#if defined(JS_64BIT)
|
||||
# define JS_BITS_PER_WORD 64
|
||||
#else
|
||||
# define JS_BITS_PER_WORD 32
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
** MACROS: JS_FUNC_TO_DATA_PTR
|
||||
** JS_DATA_TO_FUNC_PTR
|
||||
** DESCRIPTION:
|
||||
** Macros to convert between function and data pointers of the same
|
||||
** size. Use them like this:
|
||||
**
|
||||
** JSGetterOp nativeGetter;
|
||||
** JSObject* scriptedGetter;
|
||||
** ...
|
||||
** scriptedGetter = JS_FUNC_TO_DATA_PTR(JSObject*, nativeGetter);
|
||||
** ...
|
||||
** nativeGetter = JS_DATA_TO_FUNC_PTR(JSGetterOp, scriptedGetter);
|
||||
**
|
||||
***********************************************************************/
|
||||
|
||||
#define JS_FUNC_TO_DATA_PTR(type, fun) (mozilla::BitwiseCast<type>(fun))
|
||||
#define JS_DATA_TO_FUNC_PTR(type, ptr) (mozilla::BitwiseCast<type>(ptr))
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define JS_EXTENSION __extension__
|
||||
# define JS_EXTENSION_(s) __extension__ ({ s; })
|
||||
#else
|
||||
# define JS_EXTENSION
|
||||
# define JS_EXTENSION_(s) s
|
||||
#endif
|
||||
|
||||
#endif /* jstypes_h */
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef jsversion_h
|
||||
#define jsversion_h
|
||||
|
||||
/*
|
||||
* JS Capability Macros.
|
||||
*/
|
||||
#define JS_HAS_STR_HTML_HELPERS 1 /* (no longer used) */
|
||||
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
|
||||
#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
|
||||
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
|
||||
#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */
|
||||
#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */
|
||||
#define JS_HAS_CONST 1 /* (no longer used) */
|
||||
#define JS_HAS_FUN_EXPR_STMT 1 /* (no longer used) */
|
||||
#define JS_HAS_FOR_EACH_IN 1 /* has for each (lhs in iterable) */
|
||||
#define JS_HAS_GENERATORS 1 /* (no longer used) */
|
||||
#define JS_HAS_BLOCK_SCOPE 1 /* (no longer used) */
|
||||
#define JS_HAS_DESTRUCTURING 2 /* (no longer used) */
|
||||
#define JS_HAS_GENERATOR_EXPRS 1 /* (no longer used) */
|
||||
#define JS_HAS_EXPR_CLOSURES 1 /* has function (formals) listexpr */
|
||||
|
||||
/* (no longer used) */
|
||||
#define JS_HAS_NEW_GLOBAL_OBJECT 1
|
||||
|
||||
/* (no longer used) */
|
||||
#define JS_HAS_DESTRUCTURING_SHORTHAND (JS_HAS_DESTRUCTURING == 2)
|
||||
|
||||
/*
|
||||
* Feature for Object.prototype.__{define,lookup}{G,S}etter__ legacy support;
|
||||
* support likely to be made opt-in at some future time.
|
||||
*/
|
||||
#define JS_OLD_GETTER_SETTER_METHODS 1
|
||||
|
||||
#endif /* jsversion_h */
|
||||
|
|
@ -1,382 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef jswrapper_h
|
||||
#define jswrapper_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "js/Proxy.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Helper for Wrapper::New default options.
|
||||
*
|
||||
* Callers of Wrapper::New() who wish to specify a prototype for the created
|
||||
* Wrapper, *MUST* construct a WrapperOptions with a JSContext.
|
||||
*/
|
||||
class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions {
|
||||
public:
|
||||
WrapperOptions() : ProxyOptions(false),
|
||||
proto_()
|
||||
{}
|
||||
|
||||
explicit WrapperOptions(JSContext* cx) : ProxyOptions(false),
|
||||
proto_()
|
||||
{
|
||||
proto_.emplace(cx);
|
||||
}
|
||||
|
||||
inline JSObject* proto() const;
|
||||
WrapperOptions& setProto(JSObject* protoArg) {
|
||||
MOZ_ASSERT(proto_);
|
||||
*proto_ = protoArg;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
mozilla::Maybe<JS::RootedObject> proto_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A wrapper is a proxy with a target object to which it generally forwards
|
||||
* operations, but may restrict access to certain operations or augment those
|
||||
* operations in various ways.
|
||||
*
|
||||
* A wrapper can be "unwrapped" in C++, exposing the underlying object.
|
||||
* Callers should be careful to avoid unwrapping security wrappers in the wrong
|
||||
* context.
|
||||
*
|
||||
* Important: If you add a method implementation here, you probably also need
|
||||
* to add an override in CrossCompartmentWrapper. If you don't, you risk
|
||||
* compartment mismatches. See bug 945826 comment 0.
|
||||
*/
|
||||
class JS_FRIEND_API(Wrapper) : public BaseProxyHandler
|
||||
{
|
||||
unsigned mFlags;
|
||||
|
||||
public:
|
||||
explicit constexpr Wrapper(unsigned aFlags, bool aHasPrototype = false,
|
||||
bool aHasSecurityPolicy = false)
|
||||
: BaseProxyHandler(&family, aHasPrototype, aHasSecurityPolicy),
|
||||
mFlags(aFlags)
|
||||
{ }
|
||||
|
||||
virtual bool finalizeInBackground(const Value& priv) const override;
|
||||
|
||||
/* Standard internal methods. */
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const override;
|
||||
virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
|
||||
AutoIdVector& props) const override;
|
||||
virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool enumerate(JSContext* cx, HandleObject proxy,
|
||||
MutableHandleObject objp) const override;
|
||||
virtual bool getPrototype(JSContext* cx, HandleObject proxy,
|
||||
MutableHandleObject protop) const override;
|
||||
virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
|
||||
MutableHandleObject protop) const override;
|
||||
virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
|
||||
bool* succeeded) const override;
|
||||
virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
|
||||
virtual bool has(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
bool* bp) const override;
|
||||
virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp) const override;
|
||||
virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result) const override;
|
||||
virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
|
||||
virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
|
||||
|
||||
/* SpiderMonkey extensions. */
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const override;
|
||||
virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
bool* bp) const override;
|
||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
|
||||
AutoIdVector& props) const override;
|
||||
virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
|
||||
const CallArgs& args) const override;
|
||||
virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
|
||||
bool* bp) const override;
|
||||
virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject proxy,
|
||||
JS::IsArrayAnswer* answer) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||
unsigned indent) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
|
||||
RegExpGuard* g) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
|
||||
MutableHandleValue vp) const override;
|
||||
virtual bool isCallable(JSObject* obj) const override;
|
||||
virtual bool isConstructor(JSObject* obj) const override;
|
||||
virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const override;
|
||||
|
||||
public:
|
||||
using BaseProxyHandler::Action;
|
||||
|
||||
enum Flags {
|
||||
CROSS_COMPARTMENT = 1 << 0,
|
||||
LAST_USED_FLAG = CROSS_COMPARTMENT
|
||||
};
|
||||
|
||||
static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
|
||||
const WrapperOptions& options = WrapperOptions());
|
||||
|
||||
static JSObject* Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler);
|
||||
|
||||
static const Wrapper* wrapperHandler(JSObject* wrapper);
|
||||
|
||||
static JSObject* wrappedObject(JSObject* wrapper);
|
||||
|
||||
unsigned flags() const {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
static const char family;
|
||||
static const Wrapper singleton;
|
||||
static const Wrapper singletonWithPrototype;
|
||||
|
||||
static JSObject* defaultProto;
|
||||
};
|
||||
|
||||
inline JSObject*
|
||||
WrapperOptions::proto() const
|
||||
{
|
||||
return proto_ ? *proto_ : Wrapper::defaultProto;
|
||||
}
|
||||
|
||||
/* Base class for all cross compartment wrapper handlers. */
|
||||
class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
|
||||
{
|
||||
public:
|
||||
explicit constexpr CrossCompartmentWrapper(unsigned aFlags, bool aHasPrototype = false,
|
||||
bool aHasSecurityPolicy = false)
|
||||
: Wrapper(CROSS_COMPARTMENT | aFlags, aHasPrototype, aHasSecurityPolicy)
|
||||
{ }
|
||||
|
||||
/* Standard internal methods. */
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const override;
|
||||
virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper,
|
||||
AutoIdVector& props) const override;
|
||||
virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool enumerate(JSContext* cx, HandleObject wrapper, MutableHandleObject objp) const override;
|
||||
virtual bool getPrototype(JSContext* cx, HandleObject proxy,
|
||||
MutableHandleObject protop) const override;
|
||||
virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
|
||||
ObjectOpResult& result) const override;
|
||||
|
||||
virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
|
||||
MutableHandleObject protop) const override;
|
||||
virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
|
||||
bool* succeeded) const override;
|
||||
virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
|
||||
virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
|
||||
virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp) const override;
|
||||
virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result) const override;
|
||||
virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
|
||||
virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
|
||||
|
||||
/* SpiderMonkey extensions. */
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const override;
|
||||
virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
|
||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
|
||||
AutoIdVector& props) const override;
|
||||
virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
|
||||
const CallArgs& args) const override;
|
||||
virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
|
||||
bool* bp) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
|
||||
unsigned indent) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
|
||||
|
||||
// Allocate CrossCompartmentWrappers in the nursery.
|
||||
virtual bool canNurseryAllocate() const override { return true; }
|
||||
|
||||
static const CrossCompartmentWrapper singleton;
|
||||
static const CrossCompartmentWrapper singletonWithPrototype;
|
||||
};
|
||||
|
||||
class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrapper
|
||||
{
|
||||
public:
|
||||
explicit constexpr OpaqueCrossCompartmentWrapper() : CrossCompartmentWrapper(0)
|
||||
{ }
|
||||
|
||||
/* Standard internal methods. */
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const override;
|
||||
virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper,
|
||||
AutoIdVector& props) const override;
|
||||
virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool enumerate(JSContext* cx, HandleObject wrapper,
|
||||
MutableHandleObject objp) const override;
|
||||
virtual bool getPrototype(JSContext* cx, HandleObject wrapper,
|
||||
MutableHandleObject protop) const override;
|
||||
virtual bool setPrototype(JSContext* cx, HandleObject wrapper, HandleObject proto,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject wrapper, bool* isOrdinary,
|
||||
MutableHandleObject protop) const override;
|
||||
virtual bool setImmutablePrototype(JSContext* cx, HandleObject wrapper,
|
||||
bool* succeeded) const override;
|
||||
virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
|
||||
virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
bool* bp) const override;
|
||||
virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp) const override;
|
||||
virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result) const override;
|
||||
virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
|
||||
virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
|
||||
|
||||
/* SpiderMonkey extensions. */
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const override;
|
||||
virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
bool* bp) const override;
|
||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
|
||||
AutoIdVector& props) const override;
|
||||
virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject obj,
|
||||
JS::IsArrayAnswer* answer) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
|
||||
|
||||
static const OpaqueCrossCompartmentWrapper singleton;
|
||||
};
|
||||
|
||||
/*
|
||||
* Base class for security wrappers. A security wrapper is potentially hiding
|
||||
* all or part of some wrapped object thus SecurityWrapper defaults to denying
|
||||
* access to the wrappee. This is the opposite of Wrapper which tries to be
|
||||
* completely transparent.
|
||||
*
|
||||
* NB: Currently, only a few ProxyHandler operations are overridden to deny
|
||||
* access, relying on derived SecurityWrapper to block access when necessary.
|
||||
*/
|
||||
template <class Base>
|
||||
class JS_FRIEND_API(SecurityWrapper) : public Base
|
||||
{
|
||||
public:
|
||||
explicit constexpr SecurityWrapper(unsigned flags, bool hasPrototype = false)
|
||||
: Base(flags, hasPrototype, /* hasSecurityPolicy = */ true)
|
||||
{ }
|
||||
|
||||
virtual bool enter(JSContext* cx, HandleObject wrapper, HandleId id, Wrapper::Action act,
|
||||
bool* bp) const override;
|
||||
|
||||
virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
|
||||
virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
|
||||
ObjectOpResult& result) const override;
|
||||
virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const override;
|
||||
|
||||
virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
|
||||
const CallArgs& args) const override;
|
||||
virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
|
||||
|
||||
// Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
|
||||
// against.
|
||||
|
||||
virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
|
||||
JS::HandleObject callable) const override;
|
||||
virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
|
||||
|
||||
/*
|
||||
* Allow our subclasses to select the superclass behavior they want without
|
||||
* needing to specify an exact superclass.
|
||||
*/
|
||||
typedef Base Permissive;
|
||||
typedef SecurityWrapper<Base> Restrictive;
|
||||
};
|
||||
|
||||
typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper;
|
||||
|
||||
extern JSObject*
|
||||
TransparentObjectWrapper(JSContext* cx, HandleObject existing, HandleObject obj);
|
||||
|
||||
inline bool
|
||||
IsWrapper(JSObject* obj)
|
||||
{
|
||||
return IsProxy(obj) && GetProxyHandler(obj)->family() == &Wrapper::family;
|
||||
}
|
||||
|
||||
// Given a JSObject, returns that object stripped of wrappers. If
|
||||
// stopAtWindowProxy is true, then this returns the WindowProxy if it was
|
||||
// previously wrapped. Otherwise, this returns the first object for
|
||||
// which JSObject::isWrapper returns false.
|
||||
JS_FRIEND_API(JSObject*)
|
||||
UncheckedUnwrap(JSObject* obj, bool stopAtWindowProxy = true, unsigned* flagsp = nullptr);
|
||||
|
||||
// Given a JSObject, returns that object stripped of wrappers. At each stage,
|
||||
// the security wrapper has the opportunity to veto the unwrap. If
|
||||
// stopAtWindowProxy is true, then this returns the WindowProxy if it was
|
||||
// previously wrapped.
|
||||
JS_FRIEND_API(JSObject*)
|
||||
CheckedUnwrap(JSObject* obj, bool stopAtWindowProxy = true);
|
||||
|
||||
// Unwrap only the outermost security wrapper, with the same semantics as
|
||||
// above. This is the checked version of Wrapper::wrappedObject.
|
||||
JS_FRIEND_API(JSObject*)
|
||||
UnwrapOneChecked(JSObject* obj, bool stopAtWindowProxy = true);
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
IsCrossCompartmentWrapper(JSObject* obj);
|
||||
|
||||
void
|
||||
NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper);
|
||||
|
||||
void
|
||||
RemapWrapper(JSContext* cx, JSObject* wobj, JSObject* newTarget);
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
RemapAllWrappersForObject(JSContext* cx, JSObject* oldTarget,
|
||||
JSObject* newTarget);
|
||||
|
||||
// API to recompute all cross-compartment wrappers whose source and target
|
||||
// match the given filters.
|
||||
JS_FRIEND_API(bool)
|
||||
RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
|
||||
const CompartmentFilter& targetFilter);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jswrapper_h */
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* Functionality related to memory alignment. */
|
||||
|
||||
#ifndef mozilla_Alignment_h
|
||||
#define mozilla_Alignment_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* This class, and the corresponding macro MOZ_ALIGNOF, figures out how many
|
||||
* bytes of alignment a given type needs.
|
||||
*/
|
||||
template<typename T>
|
||||
class AlignmentFinder
|
||||
{
|
||||
struct Aligner
|
||||
{
|
||||
char mChar;
|
||||
T mT;
|
||||
};
|
||||
|
||||
public:
|
||||
static const size_t alignment = sizeof(Aligner) - sizeof(T);
|
||||
};
|
||||
|
||||
#define MOZ_ALIGNOF(T) mozilla::AlignmentFinder<T>::alignment
|
||||
|
||||
/*
|
||||
* Declare the MOZ_ALIGNED_DECL macro for declaring aligned types.
|
||||
*
|
||||
* For instance,
|
||||
*
|
||||
* MOZ_ALIGNED_DECL(char arr[2], 8);
|
||||
*
|
||||
* will declare a two-character array |arr| aligned to 8 bytes.
|
||||
*/
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define MOZ_ALIGNED_DECL(_type, _align) \
|
||||
_type __attribute__((aligned(_align)))
|
||||
#elif defined(_MSC_VER)
|
||||
# define MOZ_ALIGNED_DECL(_type, _align) \
|
||||
__declspec(align(_align)) _type
|
||||
#else
|
||||
# warning "We don't know how to align variables on this compiler."
|
||||
# define MOZ_ALIGNED_DECL(_type, _align) _type
|
||||
#endif
|
||||
|
||||
/*
|
||||
* AlignedElem<N> is a structure whose alignment is guaranteed to be at least N
|
||||
* bytes.
|
||||
*
|
||||
* We support 1, 2, 4, 8, and 16-bit alignment.
|
||||
*/
|
||||
template<size_t Align>
|
||||
struct AlignedElem;
|
||||
|
||||
/*
|
||||
* We have to specialize this template because GCC doesn't like
|
||||
* __attribute__((aligned(foo))) where foo is a template parameter.
|
||||
*/
|
||||
|
||||
template<>
|
||||
struct AlignedElem<1>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 1);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AlignedElem<2>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 2);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AlignedElem<4>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 4);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AlignedElem<8>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 8);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AlignedElem<16>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 16);
|
||||
};
|
||||
|
||||
/*
|
||||
* This utility pales in comparison to Boost's aligned_storage. The utility
|
||||
* simply assumes that uint64_t is enough alignment for anyone. This may need
|
||||
* to be extended one day...
|
||||
*
|
||||
* As an important side effect, pulling the storage into this template is
|
||||
* enough obfuscation to confuse gcc's strict-aliasing analysis into not giving
|
||||
* false negatives when we cast from the char buffer to whatever type we've
|
||||
* constructed using the bytes.
|
||||
*/
|
||||
template<size_t Nbytes>
|
||||
struct AlignedStorage
|
||||
{
|
||||
union U
|
||||
{
|
||||
char mBytes[Nbytes];
|
||||
uint64_t mDummy;
|
||||
} u;
|
||||
|
||||
const void* addr() const { return u.mBytes; }
|
||||
void* addr() { return u.mBytes; }
|
||||
|
||||
AlignedStorage() = default;
|
||||
|
||||
// AlignedStorage is non-copyable: the default copy constructor violates
|
||||
// strict aliasing rules, per bug 1269319.
|
||||
AlignedStorage(const AlignedStorage&) = delete;
|
||||
void operator=(const AlignedStorage&) = delete;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS AlignedStorage2
|
||||
{
|
||||
union U
|
||||
{
|
||||
char mBytes[sizeof(T)];
|
||||
uint64_t mDummy;
|
||||
} u;
|
||||
|
||||
const T* addr() const { return reinterpret_cast<const T*>(u.mBytes); }
|
||||
T* addr() { return static_cast<T*>(static_cast<void*>(u.mBytes)); }
|
||||
|
||||
AlignedStorage2() = default;
|
||||
|
||||
// AlignedStorage2 is non-copyable: the default copy constructor violates
|
||||
// strict aliasing rules, per bug 1269319.
|
||||
AlignedStorage2(const AlignedStorage2&) = delete;
|
||||
void operator=(const AlignedStorage2&) = delete;
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_Alignment_h */
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/*
|
||||
* An allocation policy concept, usable for structures and algorithms to
|
||||
* control how memory is allocated and how failures are handled.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_AllocPolicy_h
|
||||
#define mozilla_AllocPolicy_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TemplateLib.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* Allocation policies are used to implement the standard allocation behaviors
|
||||
* in a customizable way. Additionally, custom behaviors may be added to these
|
||||
* behaviors, such as additionally reporting an error through an out-of-band
|
||||
* mechanism when OOM occurs. The concept modeled here is as follows:
|
||||
*
|
||||
* - public copy constructor, assignment, destructor
|
||||
* - template <typename T> T* maybe_pod_malloc(size_t)
|
||||
* Fallible, but doesn't report an error on OOM.
|
||||
* - template <typename T> T* maybe_pod_calloc(size_t)
|
||||
* Fallible, but doesn't report an error on OOM.
|
||||
* - template <typename T> T* maybe_pod_realloc(T*, size_t, size_t)
|
||||
* Fallible, but doesn't report an error on OOM. The old allocation
|
||||
* size is passed in, in addition to the new allocation size requested.
|
||||
* - template <typename T> T* pod_malloc(size_t)
|
||||
* Responsible for OOM reporting when null is returned.
|
||||
* - template <typename T> T* pod_calloc(size_t)
|
||||
* Responsible for OOM reporting when null is returned.
|
||||
* - template <typename T> T* pod_realloc(T*, size_t, size_t)
|
||||
* Responsible for OOM reporting when null is returned. The old allocation
|
||||
* size is passed in, in addition to the new allocation size requested.
|
||||
* - void free_(void*)
|
||||
* - void reportAllocOverflow() const
|
||||
* Called on allocation overflow (that is, an allocation implicitly tried
|
||||
* to allocate more than the available memory space -- think allocating an
|
||||
* array of large-size objects, where N * size overflows) before null is
|
||||
* returned.
|
||||
* - bool checkSimulatedOOM() const
|
||||
* Some clients generally allocate memory yet in some circumstances won't
|
||||
* need to do so. For example, appending to a vector with a small amount of
|
||||
* inline storage generally allocates memory, but no allocation occurs
|
||||
* unless appending exceeds inline storage. But for testing purposes, it
|
||||
* can be useful to treat *every* operation as allocating.
|
||||
* Clients (such as this hypothetical append method implementation) should
|
||||
* call this method in situations that don't allocate, but could generally,
|
||||
* to support this. The default behavior should return true; more
|
||||
* complicated behavior might be to return false only after a certain
|
||||
* number of allocations-or-check-simulated-OOMs (coordinating with the
|
||||
* other AllocPolicy methods) have occurred.
|
||||
*
|
||||
* mfbt provides (and typically uses by default) only MallocAllocPolicy, which
|
||||
* does nothing more than delegate to the malloc/alloc/free functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A policy that straightforwardly uses malloc/calloc/realloc/free and adds no
|
||||
* extra behaviors.
|
||||
*/
|
||||
class MallocAllocPolicy
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
T* maybe_pod_malloc(size_t aNumElems)
|
||||
{
|
||||
if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<T*>(malloc(aNumElems * sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* maybe_pod_calloc(size_t aNumElems)
|
||||
{
|
||||
return static_cast<T*>(calloc(aNumElems, sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
|
||||
{
|
||||
if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<T*>(realloc(aPtr, aNewSize * sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* pod_malloc(size_t aNumElems)
|
||||
{
|
||||
return maybe_pod_malloc<T>(aNumElems);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* pod_calloc(size_t aNumElems)
|
||||
{
|
||||
return maybe_pod_calloc<T>(aNumElems);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
|
||||
{
|
||||
return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
|
||||
}
|
||||
|
||||
void free_(void* aPtr)
|
||||
{
|
||||
free(aPtr);
|
||||
}
|
||||
|
||||
void reportAllocOverflow() const
|
||||
{
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool checkSimulatedOOM() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_AllocPolicy_h */
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* Typed temporary pointers for reference-counted smart pointers. */
|
||||
|
||||
#ifndef AlreadyAddRefed_h
|
||||
#define AlreadyAddRefed_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct unused_t;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
/**
|
||||
* already_AddRefed cooperates with reference counting smart pointers to enable
|
||||
* you to assign in a pointer _without_ |AddRef|ing it. You might want to use
|
||||
* this as a return type from a function that returns an already |AddRef|ed
|
||||
* pointer.
|
||||
*
|
||||
* TODO Move already_AddRefed to namespace mozilla. This has not yet been done
|
||||
* because of the sheer number of usages of already_AddRefed.
|
||||
*/
|
||||
template<class T>
|
||||
struct MOZ_MUST_USE_TYPE MOZ_NON_AUTOABLE already_AddRefed
|
||||
{
|
||||
/*
|
||||
* We want to allow returning nullptr from functions returning
|
||||
* already_AddRefed<T>, for simplicity. But we also don't want to allow
|
||||
* returning raw T*, instead preferring creation of already_AddRefed<T> from
|
||||
* a reference counting smart pointer.
|
||||
*
|
||||
* We address the latter requirement by making the (T*) constructor explicit.
|
||||
* But |return nullptr| won't consider an explicit constructor, so we need
|
||||
* another constructor to handle it. Plain old (decltype(nullptr)) doesn't
|
||||
* cut it, because if nullptr is emulated as __null (with type int or long),
|
||||
* passing nullptr to an int/long parameter triggers compiler warnings. We
|
||||
* need a type that no one can pass accidentally; a pointer-to-member-function
|
||||
* (where no such function exists) does the trick nicely.
|
||||
*
|
||||
* That handles the return-value case. What about for locals, argument types,
|
||||
* and so on? |already_AddRefed<T>(nullptr)| considers both overloads (and
|
||||
* the (already_AddRefed<T>&&) overload as well!), so there's an ambiguity.
|
||||
* We can target true nullptr using decltype(nullptr), but we can't target
|
||||
* emulated nullptr the same way, because passing __null to an int/long
|
||||
* parameter triggers compiler warnings. So just give up on this, and provide
|
||||
* this behavior through the default constructor.
|
||||
*
|
||||
* We can revert to simply explicit (T*) and implicit (decltype(nullptr)) when
|
||||
* nullptr no longer needs to be emulated to support the ancient b2g compiler.
|
||||
* (The () overload could also be removed, if desired, if we changed callers.)
|
||||
*/
|
||||
already_AddRefed() : mRawPtr(nullptr) {}
|
||||
|
||||
// The return and argument types here are arbitrarily selected so no
|
||||
// corresponding member function exists.
|
||||
typedef void (already_AddRefed::* MatchNullptr)(double, float);
|
||||
MOZ_IMPLICIT already_AddRefed(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}
|
||||
|
||||
explicit already_AddRefed(T* aRawPtr) : mRawPtr(aRawPtr) {}
|
||||
|
||||
// Disallow copy constructor and copy assignment operator: move semantics used instead.
|
||||
already_AddRefed(const already_AddRefed<T>& aOther) = delete;
|
||||
already_AddRefed<T>& operator=(const already_AddRefed<T>& aOther) = delete;
|
||||
|
||||
already_AddRefed(already_AddRefed<T>&& aOther) : mRawPtr(aOther.take()) {}
|
||||
|
||||
already_AddRefed<T>& operator=(already_AddRefed<T>&& aOther)
|
||||
{
|
||||
mRawPtr = aOther.take();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper is useful in cases like
|
||||
*
|
||||
* already_AddRefed<BaseClass>
|
||||
* Foo()
|
||||
* {
|
||||
* RefPtr<SubClass> x = ...;
|
||||
* return x.forget();
|
||||
* }
|
||||
*
|
||||
* The autoconversion allows one to omit the idiom
|
||||
*
|
||||
* RefPtr<BaseClass> y = x.forget();
|
||||
* return y.forget();
|
||||
*
|
||||
* Note that nsRefPtr is the XPCOM reference counting smart pointer class.
|
||||
*/
|
||||
template <typename U>
|
||||
MOZ_IMPLICIT already_AddRefed(already_AddRefed<U>&& aOther) : mRawPtr(aOther.take()) {}
|
||||
|
||||
~already_AddRefed() { MOZ_ASSERT(!mRawPtr); }
|
||||
|
||||
// Specialize the unused operator<< for already_AddRefed, to allow
|
||||
// nsCOMPtr<nsIFoo> foo;
|
||||
// Unused << foo.forget();
|
||||
// Note that nsCOMPtr is the XPCOM reference counting smart pointer class.
|
||||
friend void operator<<(const mozilla::unused_t& aUnused,
|
||||
const already_AddRefed<T>& aRhs)
|
||||
{
|
||||
auto mutableAlreadyAddRefed = const_cast<already_AddRefed<T>*>(&aRhs);
|
||||
aUnused << mutableAlreadyAddRefed->take();
|
||||
}
|
||||
|
||||
MOZ_MUST_USE T* take()
|
||||
{
|
||||
T* rawPtr = mRawPtr;
|
||||
mRawPtr = nullptr;
|
||||
return rawPtr;
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper provides a static_cast replacement for already_AddRefed, so
|
||||
* if you have
|
||||
*
|
||||
* already_AddRefed<Parent> F();
|
||||
*
|
||||
* you can write
|
||||
*
|
||||
* already_AddRefed<Child>
|
||||
* G()
|
||||
* {
|
||||
* return F().downcast<Child>();
|
||||
* }
|
||||
*/
|
||||
template<class U>
|
||||
already_AddRefed<U> downcast()
|
||||
{
|
||||
U* tmp = static_cast<U*>(mRawPtr);
|
||||
mRawPtr = nullptr;
|
||||
return already_AddRefed<U>(tmp);
|
||||
}
|
||||
|
||||
private:
|
||||
T* MOZ_OWNING_REF mRawPtr;
|
||||
};
|
||||
|
||||
#endif // AlreadyAddRefed_h
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/* -*- 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 compile-time constant-length array with bounds-checking assertions. */
|
||||
|
||||
#ifndef mozilla_Array_h
|
||||
#define mozilla_Array_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/ReverseIterator.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<typename T, size_t Length>
|
||||
class Array
|
||||
{
|
||||
T mArr[Length];
|
||||
|
||||
public:
|
||||
Array() {}
|
||||
|
||||
template <typename... Args>
|
||||
MOZ_IMPLICIT Array(Args&&... aArgs)
|
||||
: mArr{mozilla::Forward<Args>(aArgs)...}
|
||||
{
|
||||
static_assert(sizeof...(aArgs) == Length,
|
||||
"The number of arguments should be equal to the template parameter Length");
|
||||
}
|
||||
|
||||
T& operator[](size_t aIndex)
|
||||
{
|
||||
MOZ_ASSERT(aIndex < Length);
|
||||
return mArr[aIndex];
|
||||
}
|
||||
|
||||
const T& operator[](size_t aIndex) const
|
||||
{
|
||||
MOZ_ASSERT(aIndex < Length);
|
||||
return mArr[aIndex];
|
||||
}
|
||||
|
||||
typedef T* iterator;
|
||||
typedef const T* const_iterator;
|
||||
typedef ReverseIterator<T*> reverse_iterator;
|
||||
typedef ReverseIterator<const T*> const_reverse_iterator;
|
||||
|
||||
// Methods for range-based for loops.
|
||||
iterator begin() { return mArr; }
|
||||
const_iterator begin() const { return mArr; }
|
||||
const_iterator cbegin() const { return begin(); }
|
||||
iterator end() { return mArr + Length; }
|
||||
const_iterator end() const { return mArr + Length; }
|
||||
const_iterator cend() const { return end(); }
|
||||
|
||||
// Methods for reverse iterating.
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
||||
const_reverse_iterator crbegin() const { return rbegin(); }
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
||||
const_reverse_iterator crend() const { return rend(); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Array<T, 0>
|
||||
{
|
||||
public:
|
||||
T& operator[](size_t aIndex)
|
||||
{
|
||||
MOZ_CRASH("indexing into zero-length array");
|
||||
}
|
||||
|
||||
const T& operator[](size_t aIndex) const
|
||||
{
|
||||
MOZ_CRASH("indexing into zero-length array");
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_Array_h */
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
/* -*- 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 helper functions related to arrays.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_ArrayUtils_h
|
||||
#define mozilla_ArrayUtils_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "mozilla/Alignment.h"
|
||||
#include "mozilla/Array.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* Safely subtract two pointers when it is known that aEnd >= aBegin, yielding a
|
||||
* size_t result.
|
||||
*
|
||||
* Ordinary pointer subtraction yields a ptrdiff_t result, which, being signed,
|
||||
* has insufficient range to express the distance between pointers at opposite
|
||||
* ends of the address space. Furthermore, most compilers use ptrdiff_t to
|
||||
* represent the intermediate byte address distance, before dividing by
|
||||
* sizeof(T); if that intermediate result overflows, they'll produce results
|
||||
* with the wrong sign even when the correct scaled distance would fit in a
|
||||
* ptrdiff_t.
|
||||
*/
|
||||
template<class T>
|
||||
MOZ_ALWAYS_INLINE size_t
|
||||
PointerRangeSize(T* aBegin, T* aEnd)
|
||||
{
|
||||
MOZ_ASSERT(aEnd >= aBegin);
|
||||
return (size_t(aEnd) - size_t(aBegin)) / sizeof(T);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the length of an array with constant length. (Use of this method
|
||||
* with a non-array pointer will not compile.)
|
||||
*
|
||||
* Beware of the implicit trailing '\0' when using this with string constants.
|
||||
*/
|
||||
template<typename T, size_t N>
|
||||
constexpr size_t
|
||||
ArrayLength(T (&aArr)[N])
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
constexpr size_t
|
||||
ArrayLength(const Array<T, N>& aArr)
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
template<typename E, E N, typename T>
|
||||
constexpr size_t
|
||||
ArrayLength(const EnumeratedArray<E, N, T>& aArr)
|
||||
{
|
||||
return size_t(N);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the address one past the last element of a constant-length array.
|
||||
*
|
||||
* Beware of the implicit trailing '\0' when using this with string constants.
|
||||
*/
|
||||
template<typename T, size_t N>
|
||||
constexpr T*
|
||||
ArrayEnd(T (&aArr)[N])
|
||||
{
|
||||
return aArr + ArrayLength(aArr);
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
constexpr T*
|
||||
ArrayEnd(Array<T, N>& aArr)
|
||||
{
|
||||
return &aArr[0] + ArrayLength(aArr);
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
constexpr const T*
|
||||
ArrayEnd(const Array<T, N>& aArr)
|
||||
{
|
||||
return &aArr[0] + ArrayLength(aArr);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename AlignType, typename Pointee,
|
||||
typename = EnableIf<!IsVoid<AlignType>::value>>
|
||||
struct AlignedChecker
|
||||
{
|
||||
static void
|
||||
test(const Pointee* aPtr)
|
||||
{
|
||||
MOZ_ASSERT((uintptr_t(aPtr) % MOZ_ALIGNOF(AlignType)) == 0,
|
||||
"performing a range-check with a misaligned pointer");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename AlignType, typename Pointee>
|
||||
struct AlignedChecker<AlignType, Pointee>
|
||||
{
|
||||
static void
|
||||
test(const Pointee* aPtr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Determines whether |aPtr| points at an object in the range [aBegin, aEnd).
|
||||
*
|
||||
* |aPtr| must have the same alignment as |aBegin| and |aEnd|. This usually
|
||||
* should be achieved by ensuring |aPtr| points at a |U|, not just that it
|
||||
* points at a |T|.
|
||||
*
|
||||
* It is a usage error for any argument to be misaligned.
|
||||
*
|
||||
* It's okay for T* to be void*, and if so U* may also be void*. In the latter
|
||||
* case no argument is required to be aligned (obviously, as void* implies no
|
||||
* particular alignment).
|
||||
*/
|
||||
template<typename T, typename U>
|
||||
inline typename EnableIf<IsSame<T, U>::value ||
|
||||
IsBaseOf<T, U>::value ||
|
||||
IsVoid<T>::value,
|
||||
bool>::Type
|
||||
IsInRange(const T* aPtr, const U* aBegin, const U* aEnd)
|
||||
{
|
||||
MOZ_ASSERT(aBegin <= aEnd);
|
||||
detail::AlignedChecker<U, T>::test(aPtr);
|
||||
detail::AlignedChecker<U, U>::test(aBegin);
|
||||
detail::AlignedChecker<U, U>::test(aEnd);
|
||||
return aBegin <= reinterpret_cast<const U*>(aPtr) &&
|
||||
reinterpret_cast<const U*>(aPtr) < aEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience version of the above method when the valid range is specified as
|
||||
* uintptr_t values. As above, |aPtr| must be aligned, and |aBegin| and |aEnd|
|
||||
* must be aligned with respect to |T|.
|
||||
*/
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsInRange(const T* aPtr, uintptr_t aBegin, uintptr_t aEnd)
|
||||
{
|
||||
return IsInRange(aPtr,
|
||||
reinterpret_cast<const T*>(aBegin),
|
||||
reinterpret_cast<const T*>(aEnd));
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* Helper for the MOZ_ARRAY_LENGTH() macro to make the length a typesafe
|
||||
* compile-time constant even on compilers lacking constexpr support.
|
||||
*/
|
||||
template <typename T, size_t N>
|
||||
char (&ArrayLengthHelper(T (&array)[N]))[N];
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* MOZ_ARRAY_LENGTH() is an alternative to mozilla::ArrayLength() for C files
|
||||
* that can't use C++ template functions and for static_assert() calls that
|
||||
* can't call ArrayLength() when it is not a C++11 constexpr function.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
# define MOZ_ARRAY_LENGTH(array) sizeof(mozilla::detail::ArrayLengthHelper(array))
|
||||
#else
|
||||
# define MOZ_ARRAY_LENGTH(array) (sizeof(array)/sizeof((array)[0]))
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_ArrayUtils_h */
|
||||
|
|
@ -1,585 +0,0 @@
|
|||
/* -*- 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
|
||||
|
||||
#if defined(MOZ_HAS_MOZGLUE) || defined(MOZILLA_INTERNAL_API)
|
||||
/*
|
||||
* 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
|
||||
|
||||
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 WIN32
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
__declspec(dllimport) int __stdcall
|
||||
TerminateProcess(void* hProcess, unsigned int uExitCode);
|
||||
__declspec(dllimport) void* __stdcall GetCurrentProcess(void);
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
#else
|
||||
# include <signal.h>
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
# include <android/log.h>
|
||||
#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.
|
||||
*/
|
||||
# if defined(__GNUC__)
|
||||
# define MOZ_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
|
||||
# else
|
||||
# define MOZ_STATIC_ASSERT_UNUSED_ATTRIBUTE /* nothing */
|
||||
# endif
|
||||
# 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_STATIC_ASSERT_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_STATIC_ASSERT_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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static MOZ_COLD MOZ_ALWAYS_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
|
||||
}
|
||||
|
||||
static MOZ_COLD MOZ_ALWAYS_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 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.
|
||||
*
|
||||
* We follow TerminateProcess() with a call to MOZ_NoReturn() so that the
|
||||
* compiler doesn't hassle us to provide a return statement after a
|
||||
* MOZ_REALLY_CRASH() call.
|
||||
*
|
||||
* (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.)
|
||||
*/
|
||||
|
||||
__declspec(noreturn) __inline void MOZ_NoReturn() {}
|
||||
|
||||
# ifdef __cplusplus
|
||||
# define MOZ_REALLY_CRASH() \
|
||||
do { \
|
||||
::__debugbreak(); \
|
||||
*((volatile int*) NULL) = __LINE__; \
|
||||
::TerminateProcess(::GetCurrentProcess(), 3); \
|
||||
::MOZ_NoReturn(); \
|
||||
} while (0)
|
||||
# else
|
||||
# define MOZ_REALLY_CRASH() \
|
||||
do { \
|
||||
__debugbreak(); \
|
||||
*((volatile int*) NULL) = __LINE__; \
|
||||
TerminateProcess(GetCurrentProcess(), 3); \
|
||||
MOZ_NoReturn(); \
|
||||
} while (0)
|
||||
# endif
|
||||
#else
|
||||
# ifdef __cplusplus
|
||||
# define MOZ_REALLY_CRASH() \
|
||||
do { \
|
||||
*((volatile int*) NULL) = __LINE__; \
|
||||
::abort(); \
|
||||
} while (0)
|
||||
# else
|
||||
# define MOZ_REALLY_CRASH() \
|
||||
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(); \
|
||||
} while (0)
|
||||
#else
|
||||
# define MOZ_CRASH(...) \
|
||||
do { \
|
||||
MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
/* 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_ReportAssertionFailure(#expr, __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ")"); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
} \
|
||||
} 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_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ") (" explain ")"); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
} \
|
||||
} 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 */
|
||||
|
||||
#ifdef RELEASE_OR_BETA
|
||||
# define MOZ_DIAGNOSTIC_ASSERT MOZ_ASSERT
|
||||
#else
|
||||
# define MOZ_DIAGNOSTIC_ASSERT MOZ_RELEASE_ASSERT
|
||||
#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)
|
||||
#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)
|
||||
#endif
|
||||
|
||||
#undef MOZ_DUMP_ASSERTION_STACK
|
||||
#undef MOZ_CRASH_CRASHREPORT
|
||||
|
||||
#endif /* mozilla_Assertions_h */
|
||||
|
|
@ -1,800 +0,0 @@
|
|||
/* -*- 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 <stdint.h>
|
||||
|
||||
/*
|
||||
* Our minimum deployment target on clang/OS X is OS X 10.6, whose SDK
|
||||
* does not have <atomic>. So be sure to check for <atomic> support
|
||||
* along with C++0x support.
|
||||
*/
|
||||
#if defined(_MSC_VER)
|
||||
# define MOZ_HAVE_CXX11_ATOMICS
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
/*
|
||||
* Clang doesn't like <atomic> from libstdc++ before 4.7 due to the
|
||||
* loose typing of the atomic builtins. GCC 4.5 and 4.6 lacks inline
|
||||
* definitions for unspecialized std::atomic and causes linking errors.
|
||||
* Therefore, we require at least 4.7.0 for using libstdc++.
|
||||
*
|
||||
* libc++ <atomic> is only functional with clang.
|
||||
*/
|
||||
# if MOZ_USING_LIBSTDCXX && MOZ_LIBSTDCXX_VERSION_AT_LEAST(4, 7, 0)
|
||||
# define MOZ_HAVE_CXX11_ATOMICS
|
||||
# elif MOZ_USING_LIBCXX && defined(__clang__)
|
||||
# define MOZ_HAVE_CXX11_ATOMICS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
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 mozilla
|
||||
|
||||
// Build up the underlying intrinsics.
|
||||
#ifdef MOZ_HAVE_CXX11_ATOMICS
|
||||
|
||||
# include <atomic>
|
||||
|
||||
namespace mozilla {
|
||||
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; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* The __sync_* family of intrinsics is documented here:
|
||||
*
|
||||
* http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Atomic-Builtins.html
|
||||
*
|
||||
* While these intrinsics are deprecated in favor of the newer __atomic_*
|
||||
* family of intrincs:
|
||||
*
|
||||
* http://gcc.gnu.org/onlinedocs/gcc-4.7.3/gcc/_005f_005fatomic-Builtins.html
|
||||
*
|
||||
* any GCC version that supports the __atomic_* intrinsics will also support
|
||||
* the <atomic> header and so will be handled above. We provide a version of
|
||||
* atomics using the __sync_* intrinsics to support older versions of GCC.
|
||||
*
|
||||
* All __sync_* intrinsics that we use below act as full memory barriers, for
|
||||
* both compiler and hardware reordering, except for __sync_lock_test_and_set,
|
||||
* which is a only an acquire barrier. When we call __sync_lock_test_and_set,
|
||||
* we add a barrier above it as appropriate.
|
||||
*/
|
||||
|
||||
template<MemoryOrdering Order> struct Barrier;
|
||||
|
||||
/*
|
||||
* Some processors (in particular, x86) don't require quite so many calls to
|
||||
* __sync_sychronize as our specializations of Barrier produce. If
|
||||
* performance turns out to be an issue, defining these specializations
|
||||
* on a per-processor basis would be a good first tuning step.
|
||||
*/
|
||||
|
||||
template<>
|
||||
struct Barrier<Relaxed>
|
||||
{
|
||||
static void beforeLoad() {}
|
||||
static void afterLoad() {}
|
||||
static void beforeStore() {}
|
||||
static void afterStore() {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Barrier<ReleaseAcquire>
|
||||
{
|
||||
static void beforeLoad() {}
|
||||
static void afterLoad() { __sync_synchronize(); }
|
||||
static void beforeStore() { __sync_synchronize(); }
|
||||
static void afterStore() {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Barrier<SequentiallyConsistent>
|
||||
{
|
||||
static void beforeLoad() { __sync_synchronize(); }
|
||||
static void afterLoad() { __sync_synchronize(); }
|
||||
static void beforeStore() { __sync_synchronize(); }
|
||||
static void afterStore() { __sync_synchronize(); }
|
||||
};
|
||||
|
||||
template<typename T, bool TIsEnum = IsEnum<T>::value>
|
||||
struct AtomicStorageType
|
||||
{
|
||||
// For non-enums, just use the type directly.
|
||||
typedef T Type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct AtomicStorageType<T, true>
|
||||
: Conditional<sizeof(T) == 4, uint32_t, uint64_t>
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8,
|
||||
"wrong type computed in conditional above");
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicMemoryOps
|
||||
{
|
||||
typedef typename AtomicStorageType<T>::Type ValueType;
|
||||
|
||||
static T load(const ValueType& aPtr)
|
||||
{
|
||||
Barrier<Order>::beforeLoad();
|
||||
T val = T(aPtr);
|
||||
Barrier<Order>::afterLoad();
|
||||
return val;
|
||||
}
|
||||
|
||||
static void store(ValueType& aPtr, T aVal)
|
||||
{
|
||||
Barrier<Order>::beforeStore();
|
||||
aPtr = ValueType(aVal);
|
||||
Barrier<Order>::afterStore();
|
||||
}
|
||||
|
||||
static T exchange(ValueType& aPtr, T aVal)
|
||||
{
|
||||
// __sync_lock_test_and_set is only an acquire barrier; loads and stores
|
||||
// can't be moved up from after to before it, but they can be moved down
|
||||
// from before to after it. We may want a stricter ordering, so we need
|
||||
// an explicit barrier.
|
||||
Barrier<Order>::beforeStore();
|
||||
return T(__sync_lock_test_and_set(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
|
||||
static bool compareExchange(ValueType& aPtr, T aOldVal, T aNewVal)
|
||||
{
|
||||
return __sync_bool_compare_and_swap(&aPtr, ValueType(aOldVal), ValueType(aNewVal));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub
|
||||
: public IntrinsicMemoryOps<T, Order>
|
||||
{
|
||||
typedef IntrinsicMemoryOps<T, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
static T add(ValueType& aPtr, T aVal)
|
||||
{
|
||||
return T(__sync_fetch_and_add(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
|
||||
static T sub(ValueType& aPtr, T aVal)
|
||||
{
|
||||
return T(__sync_fetch_and_sub(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub<T*, Order>
|
||||
: public IntrinsicMemoryOps<T*, Order>
|
||||
{
|
||||
typedef IntrinsicMemoryOps<T*, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
/*
|
||||
* The reinterpret_casts are needed so that
|
||||
* __sync_fetch_and_{add,sub} will properly type-check.
|
||||
*
|
||||
* Also, these functions do not provide standard semantics for
|
||||
* pointer types, so we need to adjust the addend.
|
||||
*/
|
||||
static ValueType add(ValueType& aPtr, ptrdiff_t aVal)
|
||||
{
|
||||
ValueType amount = reinterpret_cast<ValueType>(aVal * sizeof(T));
|
||||
return __sync_fetch_and_add(&aPtr, amount);
|
||||
}
|
||||
|
||||
static ValueType sub(ValueType& aPtr, ptrdiff_t aVal)
|
||||
{
|
||||
ValueType amount = reinterpret_cast<ValueType>(aVal * sizeof(T));
|
||||
return __sync_fetch_and_sub(&aPtr, amount);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
|
||||
{
|
||||
typedef IntrinsicAddSub<T, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
static T inc(ValueType& aPtr) { return Base::add(aPtr, 1); }
|
||||
static T dec(ValueType& aPtr) { return Base::sub(aPtr, 1); }
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics : public IntrinsicIncDec<T, Order>
|
||||
{
|
||||
static T or_( T& aPtr, T aVal) { return __sync_fetch_and_or(&aPtr, aVal); }
|
||||
static T xor_(T& aPtr, T aVal) { return __sync_fetch_and_xor(&aPtr, aVal); }
|
||||
static T and_(T& aPtr, T aVal) { return __sync_fetch_and_and(&aPtr, aVal); }
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics<T*, Order> : public IntrinsicIncDec<T*, Order>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T, bool TIsEnum = IsEnum<T>::value>
|
||||
struct ToStorageTypeArgument
|
||||
{
|
||||
typedef typename AtomicStorageType<T>::Type ResultType;
|
||||
|
||||
static constexpr ResultType convert (T aT) { return ResultType(aT); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ToStorageTypeArgument<T, false>
|
||||
{
|
||||
static constexpr T convert (T aT) { return aT; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
#else
|
||||
# error "Atomic compiler intrinsics are not supported on your platform"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
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 */
|
||||
|
|
@ -1,604 +0,0 @@
|
|||
/* -*- 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))
|
||||
#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.
|
||||
*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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_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
|
||||
|
||||
/**
|
||||
* 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; }
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define MOZ_MUST_USE __attribute__ ((warn_unused_result))
|
||||
#else
|
||||
# define MOZ_MUST_USE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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;
|
||||
* }
|
||||
*/
|
||||
#if defined(__clang__) && __cplusplus >= 201103L
|
||||
/* clang's fallthrough annotations are only available starting in C++11. */
|
||||
# define MOZ_FALLTHROUGH [[clang::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
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/*
|
||||
* 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_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_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_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.
|
||||
*/
|
||||
#ifdef MOZ_CLANG_PLUGIN
|
||||
# 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_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_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")))
|
||||
/*
|
||||
* 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_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_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_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 */
|
||||
#endif /* MOZ_CLANG_PLUGIN */
|
||||
|
||||
#define MOZ_RAII MOZ_NON_TEMPORARY_CLASS MOZ_STACK_CLASS
|
||||
|
||||
/*
|
||||
* MOZ_HAVE_REF_QUALIFIERS is defined for compilers that support C++11's rvalue
|
||||
* qualifier, "&&".
|
||||
*/
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
# define MOZ_HAVE_REF_QUALIFIERS
|
||||
#elif defined(__clang__)
|
||||
// All supported Clang versions
|
||||
# define MOZ_HAVE_REF_QUALIFIERS
|
||||
#elif defined(__GNUC__)
|
||||
# include "mozilla/Compiler.h"
|
||||
# if MOZ_GCC_VERSION_AT_LEAST(4, 8, 1)
|
||||
# define MOZ_HAVE_REF_QUALIFIERS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#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)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck) \
|
||||
__attribute__ ((format (printf, stringIndex, firstToCheck)))
|
||||
#else
|
||||
#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck)
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_Attributes_h */
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
/* -*- 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_BinarySearch_h
|
||||
#define mozilla_BinarySearch_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* The BinarySearch() algorithm searches the given container |aContainer| over
|
||||
* the sorted index range [aBegin, aEnd) for an index |i| where
|
||||
* |aContainer[i] == aTarget|.
|
||||
* If such an index |i| is found, BinarySearch returns |true| and the index is
|
||||
* returned via the outparam |aMatchOrInsertionPoint|. If no index is found,
|
||||
* BinarySearch returns |false| and the outparam returns the first index in
|
||||
* [aBegin, aEnd] where |aTarget| can be inserted to maintain sorted order.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Vector<int> sortedInts = ...
|
||||
*
|
||||
* size_t match;
|
||||
* if (BinarySearch(sortedInts, 0, sortedInts.length(), 13, &match)) {
|
||||
* printf("found 13 at %lu\n", match);
|
||||
* }
|
||||
*
|
||||
* The BinarySearchIf() version behaves similarly, but takes |aComparator|, a
|
||||
* functor to compare the values with, instead of a value to find.
|
||||
* That functor should take one argument - the value to compare - and return an
|
||||
* |int| with the comparison result:
|
||||
*
|
||||
* * 0, if the argument is equal to,
|
||||
* * less than 0, if the argument is greater than,
|
||||
* * greater than 0, if the argument is less than
|
||||
*
|
||||
* the value.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* struct Comparator {
|
||||
* int operator()(int aVal) const {
|
||||
* if (mTarget < aVal) { return -1; }
|
||||
* if (mTarget > aVal) { return 1; }
|
||||
* return 0;
|
||||
* }
|
||||
* explicit Comparator(int aTarget) : mTarget(aTarget) {}
|
||||
* const int mTarget;
|
||||
* };
|
||||
*
|
||||
* Vector<int> sortedInts = ...
|
||||
*
|
||||
* size_t match;
|
||||
* if (BinarySearchIf(sortedInts, 0, sortedInts.length(), Comparator(13), &match)) {
|
||||
* printf("found 13 at %lu\n", match);
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
template<typename Container, typename Comparator>
|
||||
bool
|
||||
BinarySearchIf(const Container& aContainer, size_t aBegin, size_t aEnd,
|
||||
const Comparator& aCompare, size_t* aMatchOrInsertionPoint)
|
||||
{
|
||||
MOZ_ASSERT(aBegin <= aEnd);
|
||||
|
||||
size_t low = aBegin;
|
||||
size_t high = aEnd;
|
||||
while (high != low) {
|
||||
size_t middle = low + (high - low) / 2;
|
||||
|
||||
// Allow any intermediate type so long as it provides a suitable ordering
|
||||
// relation.
|
||||
const int result = aCompare(aContainer[middle]);
|
||||
|
||||
if (result == 0) {
|
||||
*aMatchOrInsertionPoint = middle;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
high = middle;
|
||||
} else {
|
||||
low = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
*aMatchOrInsertionPoint = low;
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class BinarySearchDefaultComparator
|
||||
{
|
||||
public:
|
||||
explicit BinarySearchDefaultComparator(const T& aTarget)
|
||||
: mTarget(aTarget)
|
||||
{}
|
||||
|
||||
template <class U>
|
||||
int operator()(const U& aVal) const {
|
||||
if (mTarget == aVal) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mTarget < aVal) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private:
|
||||
const T& mTarget;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename Container, typename T>
|
||||
bool
|
||||
BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd,
|
||||
T aTarget, size_t* aMatchOrInsertionPoint)
|
||||
{
|
||||
return BinarySearchIf(aContainer, aBegin, aEnd,
|
||||
detail::BinarySearchDefaultComparator<T>(aTarget),
|
||||
aMatchOrInsertionPoint);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_BinarySearch_h
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
/* -*- 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 counting Bloom filter implementation. This allows consumers to
|
||||
* do fast probabilistic "is item X in set Y?" testing which will
|
||||
* never answer "no" when the correct answer is "yes" (but might
|
||||
* incorrectly answer "yes" when the correct answer is "no").
|
||||
*/
|
||||
|
||||
#ifndef mozilla_BloomFilter_h
|
||||
#define mozilla_BloomFilter_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* This class implements a counting Bloom filter as described at
|
||||
* <http://en.wikipedia.org/wiki/Bloom_filter#Counting_filters>, with
|
||||
* 8-bit counters. This allows quick probabilistic answers to the
|
||||
* question "is object X in set Y?" where the contents of Y might not
|
||||
* be time-invariant. The probabilistic nature of the test means that
|
||||
* sometimes the answer will be "yes" when it should be "no". If the
|
||||
* answer is "no", then X is guaranteed not to be in Y.
|
||||
*
|
||||
* The filter is parametrized on KeySize, which is the size of the key
|
||||
* generated by each of hash functions used by the filter, in bits,
|
||||
* and the type of object T being added and removed. T must implement
|
||||
* a |uint32_t hash() const| method which returns a uint32_t hash key
|
||||
* that will be used to generate the two separate hash functions for
|
||||
* the Bloom filter. This hash key MUST be well-distributed for good
|
||||
* results! KeySize is not allowed to be larger than 16.
|
||||
*
|
||||
* The filter uses exactly 2**KeySize bytes of memory. From now on we
|
||||
* will refer to the memory used by the filter as M.
|
||||
*
|
||||
* The expected rate of incorrect "yes" answers depends on M and on
|
||||
* the number N of objects in set Y. As long as N is small compared
|
||||
* to M, the rate of such answers is expected to be approximately
|
||||
* 4*(N/M)**2 for this filter. In practice, if Y has a few hundred
|
||||
* elements then using a KeySize of 12 gives a reasonably low
|
||||
* incorrect answer rate. A KeySize of 12 has the additional benefit
|
||||
* of using exactly one page for the filter in typical hardware
|
||||
* configurations.
|
||||
*/
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
class BloomFilter
|
||||
{
|
||||
/*
|
||||
* A counting Bloom filter with 8-bit counters. For now we assume
|
||||
* that having two hash functions is enough, but we may revisit that
|
||||
* decision later.
|
||||
*
|
||||
* The filter uses an array with 2**KeySize entries.
|
||||
*
|
||||
* Assuming a well-distributed hash function, a Bloom filter with
|
||||
* array size M containing N elements and
|
||||
* using k hash function has expected false positive rate exactly
|
||||
*
|
||||
* $ (1 - (1 - 1/M)^{kN})^k $
|
||||
*
|
||||
* because each array slot has a
|
||||
*
|
||||
* $ (1 - 1/M)^{kN} $
|
||||
*
|
||||
* chance of being 0, and the expected false positive rate is the
|
||||
* probability that all of the k hash functions will hit a nonzero
|
||||
* slot.
|
||||
*
|
||||
* For reasonable assumptions (M large, kN large, which should both
|
||||
* hold if we're worried about false positives) about M and kN this
|
||||
* becomes approximately
|
||||
*
|
||||
* $$ (1 - \exp(-kN/M))^k $$
|
||||
*
|
||||
* For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
|
||||
* or in other words
|
||||
*
|
||||
* $$ N/M = -0.5 * \ln(1 - \sqrt(r)) $$
|
||||
*
|
||||
* where r is the false positive rate. This can be used to compute
|
||||
* the desired KeySize for a given load N and false positive rate r.
|
||||
*
|
||||
* If N/M is assumed small, then the false positive rate can
|
||||
* further be approximated as 4*N^2/M^2. So increasing KeySize by
|
||||
* 1, which doubles M, reduces the false positive rate by about a
|
||||
* factor of 4, and a false positive rate of 1% corresponds to
|
||||
* about M/N == 20.
|
||||
*
|
||||
* What this means in practice is that for a few hundred keys using a
|
||||
* KeySize of 12 gives false positive rates on the order of 0.25-4%.
|
||||
*
|
||||
* Similarly, using a KeySize of 10 would lead to a 4% false
|
||||
* positive rate for N == 100 and to quite bad false positive
|
||||
* rates for larger N.
|
||||
*/
|
||||
public:
|
||||
BloomFilter()
|
||||
{
|
||||
static_assert(KeySize <= kKeyShift, "KeySize too big");
|
||||
|
||||
// Should we have a custom operator new using calloc instead and
|
||||
// require that we're allocated via the operator?
|
||||
clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the filter. This should be done before reusing it, because
|
||||
* just removing all items doesn't clear counters that hit the upper
|
||||
* bound.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/*
|
||||
* Add an item to the filter.
|
||||
*/
|
||||
void add(const T* aValue);
|
||||
|
||||
/*
|
||||
* Remove an item from the filter.
|
||||
*/
|
||||
void remove(const T* aValue);
|
||||
|
||||
/*
|
||||
* Check whether the filter might contain an item. This can
|
||||
* sometimes return true even if the item is not in the filter,
|
||||
* but will never return false for items that are actually in the
|
||||
* filter.
|
||||
*/
|
||||
bool mightContain(const T* aValue) const;
|
||||
|
||||
/*
|
||||
* Methods for add/remove/contain when we already have a hash computed
|
||||
*/
|
||||
void add(uint32_t aHash);
|
||||
void remove(uint32_t aHash);
|
||||
bool mightContain(uint32_t aHash) const;
|
||||
|
||||
private:
|
||||
static const size_t kArraySize = (1 << KeySize);
|
||||
static const uint32_t kKeyMask = (1 << KeySize) - 1;
|
||||
static const uint32_t kKeyShift = 16;
|
||||
|
||||
static uint32_t hash1(uint32_t aHash)
|
||||
{
|
||||
return aHash & kKeyMask;
|
||||
}
|
||||
static uint32_t hash2(uint32_t aHash)
|
||||
{
|
||||
return (aHash >> kKeyShift) & kKeyMask;
|
||||
}
|
||||
|
||||
uint8_t& firstSlot(uint32_t aHash)
|
||||
{
|
||||
return mCounters[hash1(aHash)];
|
||||
}
|
||||
uint8_t& secondSlot(uint32_t aHash)
|
||||
{
|
||||
return mCounters[hash2(aHash)];
|
||||
}
|
||||
|
||||
const uint8_t& firstSlot(uint32_t aHash) const
|
||||
{
|
||||
return mCounters[hash1(aHash)];
|
||||
}
|
||||
const uint8_t& secondSlot(uint32_t aHash) const
|
||||
{
|
||||
return mCounters[hash2(aHash)];
|
||||
}
|
||||
|
||||
static bool full(const uint8_t& aSlot) { return aSlot == UINT8_MAX; }
|
||||
|
||||
uint8_t mCounters[kArraySize];
|
||||
};
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
inline void
|
||||
BloomFilter<KeySize, T>::clear()
|
||||
{
|
||||
memset(mCounters, 0, kArraySize);
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
inline void
|
||||
BloomFilter<KeySize, T>::add(uint32_t aHash)
|
||||
{
|
||||
uint8_t& slot1 = firstSlot(aHash);
|
||||
if (MOZ_LIKELY(!full(slot1))) {
|
||||
++slot1;
|
||||
}
|
||||
uint8_t& slot2 = secondSlot(aHash);
|
||||
if (MOZ_LIKELY(!full(slot2))) {
|
||||
++slot2;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
MOZ_ALWAYS_INLINE void
|
||||
BloomFilter<KeySize, T>::add(const T* aValue)
|
||||
{
|
||||
uint32_t hash = aValue->hash();
|
||||
return add(hash);
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
inline void
|
||||
BloomFilter<KeySize, T>::remove(uint32_t aHash)
|
||||
{
|
||||
// If the slots are full, we don't know whether we bumped them to be
|
||||
// there when we added or not, so just leave them full.
|
||||
uint8_t& slot1 = firstSlot(aHash);
|
||||
if (MOZ_LIKELY(!full(slot1))) {
|
||||
--slot1;
|
||||
}
|
||||
uint8_t& slot2 = secondSlot(aHash);
|
||||
if (MOZ_LIKELY(!full(slot2))) {
|
||||
--slot2;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
MOZ_ALWAYS_INLINE void
|
||||
BloomFilter<KeySize, T>::remove(const T* aValue)
|
||||
{
|
||||
uint32_t hash = aValue->hash();
|
||||
remove(hash);
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
BloomFilter<KeySize, T>::mightContain(uint32_t aHash) const
|
||||
{
|
||||
// Check that all the slots for this hash contain something
|
||||
return firstSlot(aHash) && secondSlot(aHash);
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
BloomFilter<KeySize, T>::mightContain(const T* aValue) const
|
||||
{
|
||||
uint32_t hash = aValue->hash();
|
||||
return mightContain(hash);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_BloomFilter_h */
|
||||
|
|
@ -1,517 +0,0 @@
|
|||
/* -*- 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_BufferList_h
|
||||
#define mozilla_BufferList_h
|
||||
|
||||
#include <algorithm>
|
||||
#include "mozilla/AllocPolicy.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/Types.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include <string.h>
|
||||
|
||||
// BufferList represents a sequence of buffers of data. A BufferList can choose
|
||||
// to own its buffers or not. The class handles writing to the buffers,
|
||||
// iterating over them, and reading data out. Unlike SegmentedVector, the
|
||||
// buffers may be of unequal size. Like SegmentedVector, BufferList is a nice
|
||||
// way to avoid large contiguous allocations (which can trigger OOMs).
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<typename AllocPolicy>
|
||||
class BufferList : private AllocPolicy
|
||||
{
|
||||
// Each buffer in a BufferList has a size and a capacity. The first mSize
|
||||
// bytes are initialized and the remaining |mCapacity - mSize| bytes are free.
|
||||
struct Segment
|
||||
{
|
||||
char* mData;
|
||||
size_t mSize;
|
||||
size_t mCapacity;
|
||||
|
||||
Segment(char* aData, size_t aSize, size_t aCapacity)
|
||||
: mData(aData),
|
||||
mSize(aSize),
|
||||
mCapacity(aCapacity)
|
||||
{
|
||||
}
|
||||
|
||||
Segment(const Segment&) = delete;
|
||||
Segment& operator=(const Segment&) = delete;
|
||||
|
||||
Segment(Segment&&) = default;
|
||||
Segment& operator=(Segment&&) = default;
|
||||
|
||||
char* Start() const { return mData; }
|
||||
char* End() const { return mData + mSize; }
|
||||
};
|
||||
|
||||
template<typename OtherAllocPolicy>
|
||||
friend class BufferList;
|
||||
|
||||
public:
|
||||
// For the convenience of callers, all segments are required to be a multiple
|
||||
// of 8 bytes in capacity. Also, every buffer except the last one is required
|
||||
// to be full (i.e., size == capacity). Therefore, a byte at offset N within
|
||||
// the BufferList and stored in memory at an address A will satisfy
|
||||
// (N % Align == A % Align) if Align == 2, 4, or 8.
|
||||
static const size_t kSegmentAlignment = 8;
|
||||
|
||||
// Allocate a BufferList. The BufferList will free all its buffers when it is
|
||||
// destroyed. An initial buffer of size aInitialSize and capacity
|
||||
// aInitialCapacity is allocated automatically. This data will be contiguous
|
||||
// an can be accessed via |Start()|. Subsequent buffers will be allocated with
|
||||
// capacity aStandardCapacity.
|
||||
BufferList(size_t aInitialSize,
|
||||
size_t aInitialCapacity,
|
||||
size_t aStandardCapacity,
|
||||
AllocPolicy aAP = AllocPolicy())
|
||||
: AllocPolicy(aAP),
|
||||
mOwning(true),
|
||||
mSegments(aAP),
|
||||
mSize(0),
|
||||
mStandardCapacity(aStandardCapacity)
|
||||
{
|
||||
MOZ_ASSERT(aInitialCapacity % kSegmentAlignment == 0);
|
||||
MOZ_ASSERT(aStandardCapacity % kSegmentAlignment == 0);
|
||||
|
||||
if (aInitialCapacity) {
|
||||
AllocateSegment(aInitialSize, aInitialCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
BufferList(const BufferList& aOther) = delete;
|
||||
|
||||
BufferList(BufferList&& aOther)
|
||||
: mOwning(aOther.mOwning),
|
||||
mSegments(Move(aOther.mSegments)),
|
||||
mSize(aOther.mSize),
|
||||
mStandardCapacity(aOther.mStandardCapacity)
|
||||
{
|
||||
aOther.mSegments.clear();
|
||||
aOther.mSize = 0;
|
||||
}
|
||||
|
||||
BufferList& operator=(const BufferList& aOther) = delete;
|
||||
|
||||
BufferList& operator=(BufferList&& aOther)
|
||||
{
|
||||
Clear();
|
||||
|
||||
mOwning = aOther.mOwning;
|
||||
mSegments = Move(aOther.mSegments);
|
||||
mSize = aOther.mSize;
|
||||
aOther.mSegments.clear();
|
||||
aOther.mSize = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~BufferList() { Clear(); }
|
||||
|
||||
// Returns the sum of the sizes of all the buffers.
|
||||
size_t Size() const { return mSize; }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if (mOwning) {
|
||||
for (Segment& segment : mSegments) {
|
||||
this->free_(segment.mData);
|
||||
}
|
||||
}
|
||||
mSegments.clear();
|
||||
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
// Iterates over bytes in the segments. You can advance it by as many bytes as
|
||||
// you choose.
|
||||
class IterImpl
|
||||
{
|
||||
// Invariants:
|
||||
// (0) mSegment <= bufferList.mSegments.size()
|
||||
// (1) mData <= mDataEnd
|
||||
// (2) If mSegment is not the last segment, mData < mDataEnd
|
||||
uintptr_t mSegment;
|
||||
char* mData;
|
||||
char* mDataEnd;
|
||||
|
||||
friend class BufferList;
|
||||
|
||||
public:
|
||||
explicit IterImpl(const BufferList& aBuffers)
|
||||
: mSegment(0),
|
||||
mData(nullptr),
|
||||
mDataEnd(nullptr)
|
||||
{
|
||||
if (!aBuffers.mSegments.empty()) {
|
||||
mData = aBuffers.mSegments[0].Start();
|
||||
mDataEnd = aBuffers.mSegments[0].End();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a pointer to the raw data. It is valid to access up to
|
||||
// RemainingInSegment bytes of this buffer.
|
||||
char* Data() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!Done());
|
||||
return mData;
|
||||
}
|
||||
|
||||
// Returns true if the memory in the range [Data(), Data() + aBytes) is all
|
||||
// part of one contiguous buffer.
|
||||
bool HasRoomFor(size_t aBytes) const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
|
||||
return size_t(mDataEnd - mData) >= aBytes;
|
||||
}
|
||||
|
||||
// Returns the maximum value aBytes for which HasRoomFor(aBytes) will be
|
||||
// true.
|
||||
size_t RemainingInSegment() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
|
||||
return mDataEnd - mData;
|
||||
}
|
||||
|
||||
// Advances the iterator by aBytes bytes. aBytes must be less than
|
||||
// RemainingInSegment(). If advancing by aBytes takes the iterator to the
|
||||
// end of a buffer, it will be moved to the beginning of the next buffer
|
||||
// unless it is the last buffer.
|
||||
void Advance(const BufferList& aBuffers, size_t aBytes)
|
||||
{
|
||||
const Segment& segment = aBuffers.mSegments[mSegment];
|
||||
MOZ_RELEASE_ASSERT(segment.Start() <= mData);
|
||||
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
|
||||
MOZ_RELEASE_ASSERT(mDataEnd == segment.End());
|
||||
|
||||
MOZ_RELEASE_ASSERT(HasRoomFor(aBytes));
|
||||
mData += aBytes;
|
||||
|
||||
if (mData == mDataEnd && mSegment + 1 < aBuffers.mSegments.length()) {
|
||||
mSegment++;
|
||||
const Segment& nextSegment = aBuffers.mSegments[mSegment];
|
||||
mData = nextSegment.Start();
|
||||
mDataEnd = nextSegment.End();
|
||||
MOZ_RELEASE_ASSERT(mData < mDataEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the iterator by aBytes, possibly crossing segments. This function
|
||||
// returns false if it runs out of buffers to advance through. Otherwise it
|
||||
// returns true.
|
||||
bool AdvanceAcrossSegments(const BufferList& aBuffers, size_t aBytes)
|
||||
{
|
||||
size_t bytes = aBytes;
|
||||
while (bytes) {
|
||||
size_t toAdvance = std::min(bytes, RemainingInSegment());
|
||||
if (!toAdvance) {
|
||||
return false;
|
||||
}
|
||||
Advance(aBuffers, toAdvance);
|
||||
bytes -= toAdvance;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true when the iterator reaches the end of the BufferList.
|
||||
bool Done() const
|
||||
{
|
||||
return mData == mDataEnd;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Count the bytes we would need to advance in order to reach aTarget.
|
||||
size_t BytesUntil(const BufferList& aBuffers, const IterImpl& aTarget) const {
|
||||
size_t offset = 0;
|
||||
|
||||
MOZ_ASSERT(aTarget.IsIn(aBuffers));
|
||||
|
||||
char* data = mData;
|
||||
for (uintptr_t segment = mSegment; segment < aTarget.mSegment; segment++) {
|
||||
offset += aBuffers.mSegments[segment].End() - data;
|
||||
data = aBuffers.mSegments[segment].mData;
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(IsIn(aBuffers));
|
||||
MOZ_RELEASE_ASSERT(aTarget.mData >= data);
|
||||
|
||||
offset += aTarget.mData - data;
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool IsIn(const BufferList& aBuffers) const {
|
||||
return mSegment < aBuffers.mSegments.length() &&
|
||||
mData >= aBuffers.mSegments[mSegment].mData &&
|
||||
mData < aBuffers.mSegments[mSegment].End();
|
||||
}
|
||||
};
|
||||
|
||||
// Special convenience method that returns Iter().Data().
|
||||
char* Start() { return mSegments[0].mData; }
|
||||
const char* Start() const { return mSegments[0].mData; }
|
||||
|
||||
IterImpl Iter() const { return IterImpl(*this); }
|
||||
|
||||
// Copies aSize bytes from aData into the BufferList. The storage for these
|
||||
// bytes may be split across multiple buffers. Size() is increased by aSize.
|
||||
inline bool WriteBytes(const char* aData, size_t aSize);
|
||||
|
||||
// Copies possibly non-contiguous byte range starting at aIter into
|
||||
// aData. aIter is advanced by aSize bytes. Returns false if it runs out of
|
||||
// data before aSize.
|
||||
inline bool ReadBytes(IterImpl& aIter, char* aData, size_t aSize) const;
|
||||
|
||||
// Return a new BufferList that shares storage with this BufferList. The new
|
||||
// BufferList is read-only. It allows iteration over aSize bytes starting at
|
||||
// aIter. Borrow can fail, in which case *aSuccess will be false upon
|
||||
// return. The borrowed BufferList can use a different AllocPolicy than the
|
||||
// original one. However, it is not responsible for freeing buffers, so the
|
||||
// AllocPolicy is only used for the buffer vector.
|
||||
template<typename BorrowingAllocPolicy>
|
||||
BufferList<BorrowingAllocPolicy> Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
|
||||
BorrowingAllocPolicy aAP = BorrowingAllocPolicy()) const;
|
||||
|
||||
// Return a new BufferList and move storage from this BufferList to it. The
|
||||
// new BufferList owns the buffers. Move can fail, in which case *aSuccess
|
||||
// will be false upon return. The new BufferList can use a different
|
||||
// AllocPolicy than the original one. The new OtherAllocPolicy is responsible
|
||||
// for freeing buffers, so the OtherAllocPolicy must use freeing method
|
||||
// compatible to the original one.
|
||||
template<typename OtherAllocPolicy>
|
||||
BufferList<OtherAllocPolicy> MoveFallible(bool* aSuccess, OtherAllocPolicy aAP = OtherAllocPolicy());
|
||||
|
||||
// Return a new BufferList that adopts the byte range starting at Iter so that
|
||||
// range [aIter, aIter + aSize) is transplanted to the returned BufferList.
|
||||
// Contents of the buffer before aIter + aSize is left undefined.
|
||||
// Extract can fail, in which case *aSuccess will be false upon return. The
|
||||
// moved buffers are erased from the original BufferList. In case of extract
|
||||
// fails, the original BufferList is intact. All other iterators except aIter
|
||||
// are invalidated.
|
||||
// This method requires aIter and aSize to be 8-byte aligned.
|
||||
BufferList Extract(IterImpl& aIter, size_t aSize, bool* aSuccess);
|
||||
|
||||
// Return the number of bytes from 'start' to 'end', two iterators within
|
||||
// this BufferList.
|
||||
size_t RangeLength(const IterImpl& start, const IterImpl& end) const {
|
||||
MOZ_ASSERT(start.IsIn(*this) && end.IsIn(*this));
|
||||
return start.BytesUntil(*this, end);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit BufferList(AllocPolicy aAP)
|
||||
: AllocPolicy(aAP),
|
||||
mOwning(false),
|
||||
mSize(0),
|
||||
mStandardCapacity(0)
|
||||
{
|
||||
}
|
||||
|
||||
void* AllocateSegment(size_t aSize, size_t aCapacity)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mOwning);
|
||||
|
||||
char* data = this->template pod_malloc<char>(aCapacity);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mSegments.append(Segment(data, aSize, aCapacity))) {
|
||||
this->free_(data);
|
||||
return nullptr;
|
||||
}
|
||||
mSize += aSize;
|
||||
return data;
|
||||
}
|
||||
|
||||
bool mOwning;
|
||||
Vector<Segment, 1, AllocPolicy> mSegments;
|
||||
size_t mSize;
|
||||
size_t mStandardCapacity;
|
||||
};
|
||||
|
||||
template<typename AllocPolicy>
|
||||
bool
|
||||
BufferList<AllocPolicy>::WriteBytes(const char* aData, size_t aSize)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mOwning);
|
||||
MOZ_RELEASE_ASSERT(mStandardCapacity);
|
||||
|
||||
size_t copied = 0;
|
||||
size_t remaining = aSize;
|
||||
|
||||
if (!mSegments.empty()) {
|
||||
Segment& lastSegment = mSegments.back();
|
||||
|
||||
size_t toCopy = std::min(aSize, lastSegment.mCapacity - lastSegment.mSize);
|
||||
memcpy(lastSegment.mData + lastSegment.mSize, aData, toCopy);
|
||||
lastSegment.mSize += toCopy;
|
||||
mSize += toCopy;
|
||||
|
||||
copied += toCopy;
|
||||
remaining -= toCopy;
|
||||
}
|
||||
|
||||
while (remaining) {
|
||||
size_t toCopy = std::min(remaining, mStandardCapacity);
|
||||
|
||||
void* data = AllocateSegment(toCopy, mStandardCapacity);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
memcpy(data, aData + copied, toCopy);
|
||||
|
||||
copied += toCopy;
|
||||
remaining -= toCopy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename AllocPolicy>
|
||||
bool
|
||||
BufferList<AllocPolicy>::ReadBytes(IterImpl& aIter, char* aData, size_t aSize) const
|
||||
{
|
||||
size_t copied = 0;
|
||||
size_t remaining = aSize;
|
||||
while (remaining) {
|
||||
size_t toCopy = std::min(aIter.RemainingInSegment(), remaining);
|
||||
if (!toCopy) {
|
||||
// We've run out of data in the last segment.
|
||||
return false;
|
||||
}
|
||||
memcpy(aData + copied, aIter.Data(), toCopy);
|
||||
copied += toCopy;
|
||||
remaining -= toCopy;
|
||||
|
||||
aIter.Advance(*this, toCopy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename AllocPolicy> template<typename BorrowingAllocPolicy>
|
||||
BufferList<BorrowingAllocPolicy>
|
||||
BufferList<AllocPolicy>::Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
|
||||
BorrowingAllocPolicy aAP) const
|
||||
{
|
||||
BufferList<BorrowingAllocPolicy> result(aAP);
|
||||
|
||||
size_t size = aSize;
|
||||
while (size) {
|
||||
size_t toAdvance = std::min(size, aIter.RemainingInSegment());
|
||||
|
||||
if (!toAdvance || !result.mSegments.append(typename BufferList<BorrowingAllocPolicy>::Segment(aIter.mData, toAdvance, toAdvance))) {
|
||||
*aSuccess = false;
|
||||
return result;
|
||||
}
|
||||
aIter.Advance(*this, toAdvance);
|
||||
size -= toAdvance;
|
||||
}
|
||||
|
||||
result.mSize = aSize;
|
||||
*aSuccess = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename AllocPolicy> template<typename OtherAllocPolicy>
|
||||
BufferList<OtherAllocPolicy>
|
||||
BufferList<AllocPolicy>::MoveFallible(bool* aSuccess, OtherAllocPolicy aAP)
|
||||
{
|
||||
BufferList<OtherAllocPolicy> result(0, 0, mStandardCapacity, aAP);
|
||||
|
||||
IterImpl iter = Iter();
|
||||
while (!iter.Done()) {
|
||||
size_t toAdvance = iter.RemainingInSegment();
|
||||
|
||||
if (!toAdvance || !result.mSegments.append(typename BufferList<OtherAllocPolicy>::Segment(iter.mData, toAdvance, toAdvance))) {
|
||||
*aSuccess = false;
|
||||
result.mSegments.clear();
|
||||
return result;
|
||||
}
|
||||
iter.Advance(*this, toAdvance);
|
||||
}
|
||||
|
||||
result.mSize = mSize;
|
||||
mSegments.clear();
|
||||
mSize = 0;
|
||||
*aSuccess = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename AllocPolicy>
|
||||
BufferList<AllocPolicy>
|
||||
BufferList<AllocPolicy>::Extract(IterImpl& aIter, size_t aSize, bool* aSuccess)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aSize);
|
||||
MOZ_RELEASE_ASSERT(mOwning);
|
||||
MOZ_ASSERT(aSize % kSegmentAlignment == 0);
|
||||
MOZ_ASSERT(intptr_t(aIter.mData) % kSegmentAlignment == 0);
|
||||
|
||||
IterImpl iter = aIter;
|
||||
size_t size = aSize;
|
||||
size_t toCopy = std::min(size, aIter.RemainingInSegment());
|
||||
MOZ_ASSERT(toCopy % kSegmentAlignment == 0);
|
||||
|
||||
BufferList result(0, toCopy, mStandardCapacity);
|
||||
BufferList error(0, 0, mStandardCapacity);
|
||||
|
||||
// Copy the head
|
||||
if (!result.WriteBytes(aIter.mData, toCopy)) {
|
||||
*aSuccess = false;
|
||||
return error;
|
||||
}
|
||||
iter.Advance(*this, toCopy);
|
||||
size -= toCopy;
|
||||
|
||||
// Move segments to result
|
||||
auto resultGuard = MakeScopeExit([&] {
|
||||
*aSuccess = false;
|
||||
result.mSegments.erase(result.mSegments.begin()+1, result.mSegments.end());
|
||||
});
|
||||
|
||||
size_t movedSize = 0;
|
||||
uintptr_t toRemoveStart = iter.mSegment;
|
||||
uintptr_t toRemoveEnd = iter.mSegment;
|
||||
while (!iter.Done() &&
|
||||
!iter.HasRoomFor(size)) {
|
||||
if (!result.mSegments.append(Segment(mSegments[iter.mSegment].mData,
|
||||
mSegments[iter.mSegment].mSize,
|
||||
mSegments[iter.mSegment].mCapacity))) {
|
||||
return error;
|
||||
}
|
||||
movedSize += iter.RemainingInSegment();
|
||||
size -= iter.RemainingInSegment();
|
||||
toRemoveEnd++;
|
||||
iter.Advance(*this, iter.RemainingInSegment());
|
||||
}
|
||||
|
||||
if (size) {
|
||||
if (!iter.HasRoomFor(size) ||
|
||||
!result.WriteBytes(iter.Data(), size)) {
|
||||
return error;
|
||||
}
|
||||
iter.Advance(*this, size);
|
||||
}
|
||||
|
||||
mSegments.erase(mSegments.begin() + toRemoveStart, mSegments.begin() + toRemoveEnd);
|
||||
mSize -= movedSize;
|
||||
aIter.mSegment = iter.mSegment - (toRemoveEnd - toRemoveStart);
|
||||
aIter.mData = iter.mData;
|
||||
aIter.mDataEnd = iter.mDataEnd;
|
||||
MOZ_ASSERT(aIter.mDataEnd == mSegments[aIter.mSegment].End());
|
||||
result.mSize = aSize;
|
||||
|
||||
resultGuard.release();
|
||||
*aSuccess = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_BufferList_h */
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
/* -*- 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);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_Casting_h */
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/* -*- 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_ChaosMode_h
|
||||
#define mozilla_ChaosMode_h
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
enum ChaosFeature {
|
||||
None = 0x0,
|
||||
// Altering thread scheduling.
|
||||
ThreadScheduling = 0x1,
|
||||
// Altering network request scheduling.
|
||||
NetworkScheduling = 0x2,
|
||||
// Altering timer scheduling.
|
||||
TimerScheduling = 0x4,
|
||||
// Read and write less-than-requested amounts.
|
||||
IOAmounts = 0x8,
|
||||
// Iterate over hash tables in random order.
|
||||
HashTableIteration = 0x10,
|
||||
// Randomly refuse to use cached version of image (when allowed by spec).
|
||||
ImageCache = 0x20,
|
||||
Any = 0xffffffff,
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
extern MFBT_DATA Atomic<uint32_t> gChaosModeCounter;
|
||||
extern MFBT_DATA ChaosFeature gChaosFeatures;
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* When "chaos mode" is activated, code that makes implicitly nondeterministic
|
||||
* choices is encouraged to make random and extreme choices, to test more
|
||||
* code paths and uncover bugs.
|
||||
*/
|
||||
class ChaosMode
|
||||
{
|
||||
public:
|
||||
static void SetChaosFeature(ChaosFeature aChaosFeature)
|
||||
{
|
||||
detail::gChaosFeatures = aChaosFeature;
|
||||
}
|
||||
|
||||
static bool isActive(ChaosFeature aFeature)
|
||||
{
|
||||
if (detail::gChaosModeCounter > 0) {
|
||||
return true;
|
||||
}
|
||||
return detail::gChaosFeatures & aFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the chaos mode activation level. An equivalent number of
|
||||
* calls to leaveChaosMode must be made in order to restore the original
|
||||
* chaos mode state. If the activation level is nonzero all chaos mode
|
||||
* features are activated.
|
||||
*/
|
||||
static void enterChaosMode()
|
||||
{
|
||||
detail::gChaosModeCounter++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease the chaos mode activation level. See enterChaosMode().
|
||||
*/
|
||||
static void leaveChaosMode()
|
||||
{
|
||||
MOZ_ASSERT(detail::gChaosModeCounter > 0);
|
||||
detail::gChaosModeCounter--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a somewhat (but not uniformly) random uint32_t < aBound.
|
||||
* Not to be used for anything except ChaosMode, since it's not very random.
|
||||
*/
|
||||
static uint32_t randomUint32LessThan(uint32_t aBound)
|
||||
{
|
||||
MOZ_ASSERT(aBound != 0);
|
||||
return uint32_t(rand()) % aBound;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_ChaosMode_h */
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
/* -*- 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 a UTF-16 character type. */
|
||||
|
||||
#ifndef mozilla_Char16_h
|
||||
#define mozilla_Char16_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/*
|
||||
* C++11 introduces a char16_t type and support for UTF-16 string and character
|
||||
* literals. C++11's char16_t is a distinct builtin type. Technically, char16_t
|
||||
* is a 16-bit code unit of a Unicode code point, not a "character".
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
# define MOZ_USE_CHAR16_WRAPPER
|
||||
# include <cstdint>
|
||||
/**
|
||||
* Win32 API extensively uses wchar_t, which is represented by a separated
|
||||
* builtin type than char16_t per spec. It's not the case for MSVC prior to
|
||||
* MSVC 2015, but other compilers follow the spec. We want to mix wchar_t and
|
||||
* char16_t on Windows builds. This class is supposed to make it easier. It
|
||||
* stores char16_t const pointer, but provides implicit casts for wchar_t as
|
||||
* well. On other platforms, we simply use
|
||||
* |typedef const char16_t* char16ptr_t|. Here, we want to make the class as
|
||||
* similar to this typedef, including providing some casts that are allowed
|
||||
* by the typedef.
|
||||
*/
|
||||
class char16ptr_t
|
||||
{
|
||||
private:
|
||||
const char16_t* mPtr;
|
||||
static_assert(sizeof(char16_t) == sizeof(wchar_t),
|
||||
"char16_t and wchar_t sizes differ");
|
||||
|
||||
public:
|
||||
char16ptr_t(const char16_t* aPtr) : mPtr(aPtr) {}
|
||||
char16ptr_t(const wchar_t* aPtr) :
|
||||
mPtr(reinterpret_cast<const char16_t*>(aPtr))
|
||||
{}
|
||||
|
||||
/* Without this, nullptr assignment would be ambiguous. */
|
||||
constexpr char16ptr_t(decltype(nullptr)) : mPtr(nullptr) {}
|
||||
|
||||
operator const char16_t*() const
|
||||
{
|
||||
return mPtr;
|
||||
}
|
||||
operator const wchar_t*() const
|
||||
{
|
||||
return reinterpret_cast<const wchar_t*>(mPtr);
|
||||
}
|
||||
operator const void*() const
|
||||
{
|
||||
return mPtr;
|
||||
}
|
||||
operator bool() const
|
||||
{
|
||||
return mPtr != nullptr;
|
||||
}
|
||||
|
||||
/* Explicit cast operators to allow things like (char16_t*)str. */
|
||||
explicit operator char16_t*() const
|
||||
{
|
||||
return const_cast<char16_t*>(mPtr);
|
||||
}
|
||||
explicit operator wchar_t*() const
|
||||
{
|
||||
return const_cast<wchar_t*>(static_cast<const wchar_t*>(*this));
|
||||
}
|
||||
explicit operator int() const
|
||||
{
|
||||
return reinterpret_cast<intptr_t>(mPtr);
|
||||
}
|
||||
explicit operator unsigned int() const
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(mPtr);
|
||||
}
|
||||
explicit operator long() const
|
||||
{
|
||||
return reinterpret_cast<intptr_t>(mPtr);
|
||||
}
|
||||
explicit operator unsigned long() const
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(mPtr);
|
||||
}
|
||||
explicit operator long long() const
|
||||
{
|
||||
return reinterpret_cast<intptr_t>(mPtr);
|
||||
}
|
||||
explicit operator unsigned long long() const
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(mPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some Windows API calls accept BYTE* but require that data actually be
|
||||
* WCHAR*. Supporting this requires explicit operators to support the
|
||||
* requisite explicit casts.
|
||||
*/
|
||||
explicit operator const char*() const
|
||||
{
|
||||
return reinterpret_cast<const char*>(mPtr);
|
||||
}
|
||||
explicit operator const unsigned char*() const
|
||||
{
|
||||
return reinterpret_cast<const unsigned char*>(mPtr);
|
||||
}
|
||||
explicit operator unsigned char*() const
|
||||
{
|
||||
return
|
||||
const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(mPtr));
|
||||
}
|
||||
explicit operator void*() const
|
||||
{
|
||||
return const_cast<char16_t*>(mPtr);
|
||||
}
|
||||
|
||||
/* Some operators used on pointers. */
|
||||
char16_t operator[](size_t aIndex) const
|
||||
{
|
||||
return mPtr[aIndex];
|
||||
}
|
||||
bool operator==(const char16ptr_t& aOther) const
|
||||
{
|
||||
return mPtr == aOther.mPtr;
|
||||
}
|
||||
bool operator==(decltype(nullptr)) const
|
||||
{
|
||||
return mPtr == nullptr;
|
||||
}
|
||||
bool operator!=(const char16ptr_t& aOther) const
|
||||
{
|
||||
return mPtr != aOther.mPtr;
|
||||
}
|
||||
bool operator!=(decltype(nullptr)) const
|
||||
{
|
||||
return mPtr != nullptr;
|
||||
}
|
||||
char16ptr_t operator+(int aValue) const
|
||||
{
|
||||
return char16ptr_t(mPtr + aValue);
|
||||
}
|
||||
char16ptr_t operator+(unsigned int aValue) const
|
||||
{
|
||||
return char16ptr_t(mPtr + aValue);
|
||||
}
|
||||
char16ptr_t operator+(long aValue) const
|
||||
{
|
||||
return char16ptr_t(mPtr + aValue);
|
||||
}
|
||||
char16ptr_t operator+(unsigned long aValue) const
|
||||
{
|
||||
return char16ptr_t(mPtr + aValue);
|
||||
}
|
||||
char16ptr_t operator+(long long aValue) const
|
||||
{
|
||||
return char16ptr_t(mPtr + aValue);
|
||||
}
|
||||
char16ptr_t operator+(unsigned long long aValue) const
|
||||
{
|
||||
return char16ptr_t(mPtr + aValue);
|
||||
}
|
||||
ptrdiff_t operator-(const char16ptr_t& aOther) const
|
||||
{
|
||||
return mPtr - aOther.mPtr;
|
||||
}
|
||||
};
|
||||
|
||||
inline decltype((char*)0-(char*)0)
|
||||
operator-(const char16_t* aX, const char16ptr_t aY)
|
||||
{
|
||||
return aX - static_cast<const char16_t*>(aY);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
typedef const char16_t* char16ptr_t;
|
||||
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(char16_t) == 2, "Is char16_t type 16 bits?");
|
||||
static_assert(char16_t(-1) > char16_t(0), "Is char16_t type unsigned?");
|
||||
static_assert(sizeof(u'A') == 2, "Is unicode char literal 16 bits?");
|
||||
static_assert(sizeof(u""[0]) == 2, "Is unicode string char 16 bits?");
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_Char16_h */
|
||||
|
|
@ -1,791 +0,0 @@
|
|||
/* -*- 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 run(U)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, true, true, false>
|
||||
{
|
||||
static bool 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 run(U aX)
|
||||
{
|
||||
return aX <= MaxValue<T>::value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, true, false, false>
|
||||
{
|
||||
static bool 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 run(U aX)
|
||||
{
|
||||
return sizeof(T) >= sizeof(U)
|
||||
? aX >= 0
|
||||
: aX >= 0 && aX <= U(MaxValue<T>::value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
inline 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 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 */
|
||||
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 */
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
/* -*- 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, 8, 0)
|
||||
# error "mfbt (and Gecko) require at least gcc 4.8 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 */
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
/* -*- 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 simple compression/decompression functions. */
|
||||
|
||||
#ifndef mozilla_Compression_h_
|
||||
#define mozilla_Compression_h_
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace Compression {
|
||||
|
||||
/**
|
||||
* LZ4 is a very fast byte-wise compression algorithm.
|
||||
*
|
||||
* Compared to Google's Snappy it is faster to compress and decompress and
|
||||
* generally produces output of about the same size.
|
||||
*
|
||||
* Compared to zlib it compresses at about 10x the speed, decompresses at about
|
||||
* 4x the speed and produces output of about 1.5x the size.
|
||||
*/
|
||||
|
||||
class LZ4
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Compresses |aInputSize| bytes from |aSource| into |aDest|. Destination
|
||||
* buffer must be already allocated, and must be sized to handle worst cases
|
||||
* situations (input data not compressible). Worst case size evaluation is
|
||||
* provided by function maxCompressedSize()
|
||||
*
|
||||
* @param aInputSize is the input size. Max supported value is ~1.9GB
|
||||
* @return the number of bytes written in buffer |aDest|
|
||||
*/
|
||||
static MFBT_API size_t
|
||||
compress(const char* aSource, size_t aInputSize, char* aDest);
|
||||
|
||||
/**
|
||||
* Compress |aInputSize| bytes from |aSource| into an output buffer
|
||||
* |aDest| of maximum size |aMaxOutputSize|. If it cannot achieve it,
|
||||
* compression will stop, and result of the function will be zero,
|
||||
* |aDest| will still be written to, but since the number of input
|
||||
* bytes consumed is not returned the result is not usable.
|
||||
*
|
||||
* This function never writes outside of provided output buffer.
|
||||
*
|
||||
* @param aInputSize is the input size. Max supported value is ~1.9GB
|
||||
* @param aMaxOutputSize is the size of the destination buffer (which must
|
||||
* be already allocated)
|
||||
* @return the number of bytes written in buffer |aDest| or 0 if the
|
||||
* compression fails
|
||||
*/
|
||||
static MFBT_API size_t
|
||||
compressLimitedOutput(const char* aSource, size_t aInputSize, char* aDest,
|
||||
size_t aMaxOutputSize);
|
||||
|
||||
/**
|
||||
* If the source stream is malformed, the function will stop decoding
|
||||
* and return false.
|
||||
*
|
||||
* This function never writes outside of provided buffers, and never
|
||||
* modifies input buffer.
|
||||
*
|
||||
* Note: destination buffer must be already allocated, and its size must be a
|
||||
* minimum of |aOutputSize| bytes.
|
||||
*
|
||||
* @param aOutputSize is the output size, therefore the original size
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
static MFBT_API MOZ_MUST_USE bool
|
||||
decompress(const char* aSource, char* aDest, size_t aOutputSize);
|
||||
|
||||
/**
|
||||
* If the source stream is malformed, the function will stop decoding
|
||||
* and return false.
|
||||
*
|
||||
* This function never writes beyond aDest + aMaxOutputSize, and is
|
||||
* therefore protected against malicious data packets.
|
||||
*
|
||||
* Note: Destination buffer must be already allocated. This version is
|
||||
* slightly slower than the decompress without the aMaxOutputSize.
|
||||
*
|
||||
* @param aInputSize is the length of the input compressed data
|
||||
* @param aMaxOutputSize is the size of the destination buffer (which must be
|
||||
* already allocated)
|
||||
* @param aOutputSize the actual number of bytes decoded in the destination
|
||||
* buffer (necessarily <= aMaxOutputSize)
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
static MFBT_API MOZ_MUST_USE bool
|
||||
decompress(const char* aSource, size_t aInputSize, char* aDest,
|
||||
size_t aMaxOutputSize, size_t* aOutputSize);
|
||||
|
||||
/*
|
||||
* Provides the maximum size that LZ4 may output in a "worst case"
|
||||
* scenario (input data not compressible) primarily useful for memory
|
||||
* allocation of output buffer.
|
||||
* note : this function is limited by "int" range (2^31-1)
|
||||
*
|
||||
* @param aInputSize is the input size. Max supported value is ~1.9GB
|
||||
* @return maximum output size in a "worst case" scenario
|
||||
*/
|
||||
static inline size_t maxCompressedSize(size_t aInputSize)
|
||||
{
|
||||
size_t max = (aInputSize + (aInputSize / 255) + 16);
|
||||
MOZ_ASSERT(max > aInputSize);
|
||||
return max;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace Compression */
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_Compression_h_ */
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/* -*- 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 DebugOnly, a type for variables used only in debug builds (i.e. by
|
||||
* assertions).
|
||||
*/
|
||||
|
||||
#ifndef mozilla_DebugOnly_h
|
||||
#define mozilla_DebugOnly_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* DebugOnly contains a value of type T, but only in debug builds. In release
|
||||
* builds, it does not contain a value. This helper is intended to be used with
|
||||
* MOZ_ASSERT()-style macros, allowing one to write:
|
||||
*
|
||||
* DebugOnly<bool> check = func();
|
||||
* MOZ_ASSERT(check);
|
||||
*
|
||||
* more concisely than declaring |check| conditional on #ifdef DEBUG.
|
||||
*
|
||||
* DebugOnly instances can only be coerced to T in debug builds. In release
|
||||
* builds they don't have a value, so type coercion is not well defined.
|
||||
*
|
||||
* NOTE: DebugOnly instances still take up one byte of space, plus padding, even
|
||||
* in optimized, non-DEBUG builds (see bug 1253094 comment 37 for more info).
|
||||
* For this reason the class is MOZ_STACK_CLASS to prevent consumers using
|
||||
* DebugOnly for struct/class members and unwittingly inflating the size of
|
||||
* their objects in release builds.
|
||||
*/
|
||||
template<typename T>
|
||||
class MOZ_STACK_CLASS DebugOnly
|
||||
{
|
||||
public:
|
||||
#ifdef DEBUG
|
||||
T value;
|
||||
|
||||
DebugOnly() { }
|
||||
MOZ_IMPLICIT DebugOnly(const T& aOther) : value(aOther) { }
|
||||
DebugOnly(const DebugOnly& aOther) : value(aOther.value) { }
|
||||
DebugOnly& operator=(const T& aRhs) {
|
||||
value = aRhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator++(int) { value++; }
|
||||
void operator--(int) { value--; }
|
||||
|
||||
// Do not define operator+=(), etc. here. These will coerce via the
|
||||
// implicit cast and built-in operators. Defining explicit methods here
|
||||
// will create ambiguity the compiler can't deal with.
|
||||
|
||||
T* operator&() { return &value; }
|
||||
|
||||
operator T&() { return value; }
|
||||
operator const T&() const { return value; }
|
||||
|
||||
T& operator->() { return value; }
|
||||
const T& operator->() const { return value; }
|
||||
|
||||
#else
|
||||
DebugOnly() { }
|
||||
MOZ_IMPLICIT DebugOnly(const T&) { }
|
||||
DebugOnly(const DebugOnly&) { }
|
||||
DebugOnly& operator=(const T&) { return *this; }
|
||||
void operator++(int) { }
|
||||
void operator--(int) { }
|
||||
DebugOnly& operator+=(const T&) { return *this; }
|
||||
DebugOnly& operator-=(const T&) { return *this; }
|
||||
DebugOnly& operator&=(const T&) { return *this; }
|
||||
DebugOnly& operator|=(const T&) { return *this; }
|
||||
DebugOnly& operator^=(const T&) { return *this; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DebugOnly must always have a destructor or else it will
|
||||
* generate "unused variable" warnings, exactly what it's intended
|
||||
* to avoid!
|
||||
*/
|
||||
~DebugOnly() {}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_DebugOnly_h */
|
||||
|
|
@ -1,221 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Google Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imported from:
|
||||
* https://chromium.googlesource.com/chromium/src.git/+/master/third_party/WebKit/Source/platform/Decimal.h
|
||||
* Check UPSTREAM-GIT-SHA for the commit ID of the last update from Blink core.
|
||||
*/
|
||||
|
||||
#ifndef Decimal_h
|
||||
#define Decimal_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include <stdint.h>
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef ASSERT
|
||||
#define DEFINED_ASSERT_FOR_DECIMAL_H 1
|
||||
#define ASSERT MOZ_ASSERT
|
||||
#endif
|
||||
|
||||
#define PLATFORM_EXPORT
|
||||
|
||||
// To use USING_FAST_MALLOC we'd need:
|
||||
// https://chromium.googlesource.com/chromium/src.git/+/master/third_party/WebKit/Source/wtf/Allocator.h
|
||||
// Since we don't allocate Decimal objects, no need.
|
||||
#define USING_FAST_MALLOC(type) \
|
||||
void ignore_this_dummy_method() = delete
|
||||
|
||||
#define DISALLOW_NEW() \
|
||||
private: \
|
||||
void* operator new(size_t) = delete; \
|
||||
void* operator new(size_t, void*) = delete; \
|
||||
public:
|
||||
|
||||
namespace blink {
|
||||
|
||||
namespace DecimalPrivate {
|
||||
class SpecialValueHandler;
|
||||
}
|
||||
|
||||
// This class represents decimal base floating point number.
|
||||
//
|
||||
// FIXME: Once all C++ compiler support decimal type, we should replace this
|
||||
// class to compiler supported one. See below URI for current status of decimal
|
||||
// type for C++: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1977.html
|
||||
class PLATFORM_EXPORT Decimal {
|
||||
USING_FAST_MALLOC(Decimal);
|
||||
public:
|
||||
enum Sign {
|
||||
Positive,
|
||||
Negative,
|
||||
};
|
||||
|
||||
// You should not use EncodedData other than unit testing.
|
||||
class EncodedData {
|
||||
DISALLOW_NEW();
|
||||
// For accessing FormatClass.
|
||||
friend class Decimal;
|
||||
friend class DecimalPrivate::SpecialValueHandler;
|
||||
public:
|
||||
EncodedData(Sign, int exponent, uint64_t coefficient);
|
||||
|
||||
bool operator==(const EncodedData&) const;
|
||||
bool operator!=(const EncodedData& another) const { return !operator==(another); }
|
||||
|
||||
uint64_t coefficient() const { return m_coefficient; }
|
||||
int countDigits() const;
|
||||
int exponent() const { return m_exponent; }
|
||||
bool isFinite() const { return !isSpecial(); }
|
||||
bool isInfinity() const { return m_formatClass == ClassInfinity; }
|
||||
bool isNaN() const { return m_formatClass == ClassNaN; }
|
||||
bool isSpecial() const { return m_formatClass == ClassInfinity || m_formatClass == ClassNaN; }
|
||||
bool isZero() const { return m_formatClass == ClassZero; }
|
||||
Sign sign() const { return m_sign; }
|
||||
void setSign(Sign sign) { m_sign = sign; }
|
||||
|
||||
private:
|
||||
enum FormatClass {
|
||||
ClassInfinity,
|
||||
ClassNormal,
|
||||
ClassNaN,
|
||||
ClassZero,
|
||||
};
|
||||
|
||||
EncodedData(Sign, FormatClass);
|
||||
FormatClass formatClass() const { return m_formatClass; }
|
||||
|
||||
uint64_t m_coefficient;
|
||||
int16_t m_exponent;
|
||||
FormatClass m_formatClass;
|
||||
Sign m_sign;
|
||||
};
|
||||
|
||||
MFBT_API explicit Decimal(int32_t = 0);
|
||||
MFBT_API Decimal(Sign, int exponent, uint64_t coefficient);
|
||||
MFBT_API Decimal(const Decimal&);
|
||||
|
||||
MFBT_API Decimal& operator=(const Decimal&);
|
||||
MFBT_API Decimal& operator+=(const Decimal&);
|
||||
MFBT_API Decimal& operator-=(const Decimal&);
|
||||
MFBT_API Decimal& operator*=(const Decimal&);
|
||||
MFBT_API Decimal& operator/=(const Decimal&);
|
||||
|
||||
MFBT_API Decimal operator-() const;
|
||||
|
||||
MFBT_API bool operator==(const Decimal&) const;
|
||||
MFBT_API bool operator!=(const Decimal&) const;
|
||||
MFBT_API bool operator<(const Decimal&) const;
|
||||
MFBT_API bool operator<=(const Decimal&) const;
|
||||
MFBT_API bool operator>(const Decimal&) const;
|
||||
MFBT_API bool operator>=(const Decimal&) const;
|
||||
|
||||
MFBT_API Decimal operator+(const Decimal&) const;
|
||||
MFBT_API Decimal operator-(const Decimal&) const;
|
||||
MFBT_API Decimal operator*(const Decimal&) const;
|
||||
MFBT_API Decimal operator/(const Decimal&) const;
|
||||
|
||||
int exponent() const
|
||||
{
|
||||
ASSERT(isFinite());
|
||||
return m_data.exponent();
|
||||
}
|
||||
|
||||
bool isFinite() const { return m_data.isFinite(); }
|
||||
bool isInfinity() const { return m_data.isInfinity(); }
|
||||
bool isNaN() const { return m_data.isNaN(); }
|
||||
bool isNegative() const { return sign() == Negative; }
|
||||
bool isPositive() const { return sign() == Positive; }
|
||||
bool isSpecial() const { return m_data.isSpecial(); }
|
||||
bool isZero() const { return m_data.isZero(); }
|
||||
|
||||
MFBT_API Decimal abs() const;
|
||||
MFBT_API Decimal ceil() const;
|
||||
MFBT_API Decimal floor() const;
|
||||
MFBT_API Decimal remainder(const Decimal&) const;
|
||||
MFBT_API Decimal round() const;
|
||||
|
||||
MFBT_API double toDouble() const;
|
||||
// Note: toString method supports infinity and nan but fromString not.
|
||||
MFBT_API std::string toString() const;
|
||||
MFBT_API bool toString(char* strBuf, size_t bufLength) const;
|
||||
|
||||
static MFBT_API Decimal fromDouble(double);
|
||||
// fromString supports following syntax EBNF:
|
||||
// number ::= sign? digit+ ('.' digit*) (exponent-marker sign? digit+)?
|
||||
// | sign? '.' digit+ (exponent-marker sign? digit+)?
|
||||
// sign ::= '+' | '-'
|
||||
// exponent-marker ::= 'e' | 'E'
|
||||
// digit ::= '0' | '1' | ... | '9'
|
||||
// Note: fromString doesn't support "infinity" and "nan".
|
||||
static MFBT_API Decimal fromString(const std::string& aValue);
|
||||
static MFBT_API Decimal infinity(Sign);
|
||||
static MFBT_API Decimal nan();
|
||||
static MFBT_API Decimal zero(Sign);
|
||||
|
||||
// You should not use below methods. We expose them for unit testing.
|
||||
MFBT_API explicit Decimal(const EncodedData&);
|
||||
const EncodedData& value() const { return m_data; }
|
||||
|
||||
private:
|
||||
struct AlignedOperands {
|
||||
uint64_t lhsCoefficient;
|
||||
uint64_t rhsCoefficient;
|
||||
int exponent;
|
||||
};
|
||||
|
||||
MFBT_API explicit Decimal(double);
|
||||
MFBT_API Decimal compareTo(const Decimal&) const;
|
||||
|
||||
static MFBT_API AlignedOperands alignOperands(const Decimal& lhs, const Decimal& rhs);
|
||||
static inline Sign invertSign(Sign sign) { return sign == Negative ? Positive : Negative; }
|
||||
|
||||
Sign sign() const { return m_data.sign(); }
|
||||
|
||||
EncodedData m_data;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
||||
namespace mozilla {
|
||||
typedef blink::Decimal Decimal;
|
||||
} // namespace mozilla
|
||||
|
||||
#undef USING_FAST_MALLOC
|
||||
|
||||
#ifdef DEFINED_ASSERT_FOR_DECIMAL_H
|
||||
#undef DEFINED_ASSERT_FOR_DECIMAL_H
|
||||
#undef ASSERT
|
||||
#endif
|
||||
|
||||
#endif // Decimal_h
|
||||
|
|
@ -1,695 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* Functions for reading and writing integers in various endiannesses. */
|
||||
|
||||
/*
|
||||
* The classes LittleEndian and BigEndian expose static methods for
|
||||
* reading and writing 16-, 32-, and 64-bit signed and unsigned integers
|
||||
* in their respective endianness. The naming scheme is:
|
||||
*
|
||||
* {Little,Big}Endian::{read,write}{Uint,Int}<bitsize>
|
||||
*
|
||||
* For instance, LittleEndian::readInt32 will read a 32-bit signed
|
||||
* integer from memory in little endian format. Similarly,
|
||||
* BigEndian::writeUint16 will write a 16-bit unsigned integer to memory
|
||||
* in big-endian format.
|
||||
*
|
||||
* The class NativeEndian exposes methods for conversion of existing
|
||||
* data to and from the native endianness. These methods are intended
|
||||
* for cases where data needs to be transferred, serialized, etc.
|
||||
* swap{To,From}{Little,Big}Endian byteswap a single value if necessary.
|
||||
* Bulk conversion functions are also provided which optimize the
|
||||
* no-conversion-needed case:
|
||||
*
|
||||
* - copyAndSwap{To,From}{Little,Big}Endian;
|
||||
* - swap{To,From}{Little,Big}EndianInPlace.
|
||||
*
|
||||
* The *From* variants are intended to be used for reading data and the
|
||||
* *To* variants for writing data.
|
||||
*
|
||||
* Methods on NativeEndian work with integer data of any type.
|
||||
* Floating-point data is not supported.
|
||||
*
|
||||
* For clarity in networking code, "Network" may be used as a synonym
|
||||
* for "Big" in any of the above methods or class names.
|
||||
*
|
||||
* As an example, reading a file format header whose fields are stored
|
||||
* in big-endian format might look like:
|
||||
*
|
||||
* class ExampleHeader
|
||||
* {
|
||||
* private:
|
||||
* uint32_t mMagic;
|
||||
* uint32_t mLength;
|
||||
* uint32_t mTotalRecords;
|
||||
* uint64_t mChecksum;
|
||||
*
|
||||
* public:
|
||||
* ExampleHeader(const void* data)
|
||||
* {
|
||||
* const uint8_t* ptr = static_cast<const uint8_t*>(data);
|
||||
* mMagic = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
|
||||
* mLength = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
|
||||
* mTotalRecords = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
|
||||
* mChecksum = BigEndian::readUint64(ptr);
|
||||
* }
|
||||
* ...
|
||||
* };
|
||||
*/
|
||||
|
||||
#ifndef mozilla_EndianUtils_h
|
||||
#define mozilla_EndianUtils_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <stdlib.h>
|
||||
# pragma intrinsic(_byteswap_ushort)
|
||||
# pragma intrinsic(_byteswap_ulong)
|
||||
# pragma intrinsic(_byteswap_uint64)
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
# if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# error "CPU type is unknown"
|
||||
# endif
|
||||
#elif defined(_WIN32)
|
||||
# if defined(_M_IX86)
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# elif defined(_M_ARM)
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# error "CPU type is unknown"
|
||||
# endif
|
||||
#elif defined(__APPLE__) || defined(__powerpc__) || defined(__ppc__)
|
||||
# if __LITTLE_ENDIAN__
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# elif __BIG_ENDIAN__
|
||||
# define MOZ_BIG_ENDIAN 1
|
||||
# endif
|
||||
#elif defined(__GNUC__) && \
|
||||
defined(__BYTE_ORDER__) && \
|
||||
defined(__ORDER_LITTLE_ENDIAN__) && \
|
||||
defined(__ORDER_BIG_ENDIAN__)
|
||||
/*
|
||||
* Some versions of GCC provide architecture-independent macros for
|
||||
* this. Yes, there are more than two values for __BYTE_ORDER__.
|
||||
*/
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define MOZ_BIG_ENDIAN 1
|
||||
# else
|
||||
# error "Can't handle mixed-endian architectures"
|
||||
# endif
|
||||
/*
|
||||
* We can't include useful headers like <endian.h> or <sys/isa_defs.h>
|
||||
* here because they're not present on all platforms. Instead we have
|
||||
* this big conditional that ideally will catch all the interesting
|
||||
* cases.
|
||||
*/
|
||||
#elif defined(__sparc) || defined(__sparc__) || \
|
||||
defined(_POWER) || defined(__hppa) || \
|
||||
defined(_MIPSEB) || defined(__ARMEB__) || \
|
||||
defined(__s390__) || defined(__AARCH64EB__) || \
|
||||
(defined(__sh__) && defined(__LITTLE_ENDIAN__)) || \
|
||||
(defined(__ia64) && defined(__BIG_ENDIAN__))
|
||||
# define MOZ_BIG_ENDIAN 1
|
||||
#elif defined(__i386) || defined(__i386__) || \
|
||||
defined(__x86_64) || defined(__x86_64__) || \
|
||||
defined(_MIPSEL) || defined(__ARMEL__) || \
|
||||
defined(__alpha__) || defined(__AARCH64EL__) || \
|
||||
(defined(__sh__) && defined(__BIG_ENDIAN__)) || \
|
||||
(defined(__ia64) && !defined(__BIG_ENDIAN__))
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
#endif
|
||||
|
||||
#if MOZ_BIG_ENDIAN
|
||||
# define MOZ_LITTLE_ENDIAN 0
|
||||
#elif MOZ_LITTLE_ENDIAN
|
||||
# define MOZ_BIG_ENDIAN 0
|
||||
#else
|
||||
# error "Cannot determine endianness"
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# if __has_builtin(__builtin_bswap16)
|
||||
# define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
# define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16
|
||||
#elif defined(_MSC_VER)
|
||||
# define MOZ_HAVE_BUILTIN_BYTESWAP16 _byteswap_ushort
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* We need wrappers here because free functions with default template
|
||||
* arguments and/or partial specialization of function templates are not
|
||||
* supported by all the compilers we use.
|
||||
*/
|
||||
template<typename T, size_t Size = sizeof(T)>
|
||||
struct Swapper;
|
||||
|
||||
template<typename T>
|
||||
struct Swapper<T, 2>
|
||||
{
|
||||
static T swap(T aValue)
|
||||
{
|
||||
#if defined(MOZ_HAVE_BUILTIN_BYTESWAP16)
|
||||
return MOZ_HAVE_BUILTIN_BYTESWAP16(aValue);
|
||||
#else
|
||||
return T(((aValue & 0x00ff) << 8) | ((aValue & 0xff00) >> 8));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Swapper<T, 4>
|
||||
{
|
||||
static T swap(T aValue)
|
||||
{
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
return T(__builtin_bswap32(aValue));
|
||||
#elif defined(_MSC_VER)
|
||||
return T(_byteswap_ulong(aValue));
|
||||
#else
|
||||
return T(((aValue & 0x000000ffU) << 24) |
|
||||
((aValue & 0x0000ff00U) << 8) |
|
||||
((aValue & 0x00ff0000U) >> 8) |
|
||||
((aValue & 0xff000000U) >> 24));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Swapper<T, 8>
|
||||
{
|
||||
static inline T swap(T aValue)
|
||||
{
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
return T(__builtin_bswap64(aValue));
|
||||
#elif defined(_MSC_VER)
|
||||
return T(_byteswap_uint64(aValue));
|
||||
#else
|
||||
return T(((aValue & 0x00000000000000ffULL) << 56) |
|
||||
((aValue & 0x000000000000ff00ULL) << 40) |
|
||||
((aValue & 0x0000000000ff0000ULL) << 24) |
|
||||
((aValue & 0x00000000ff000000ULL) << 8) |
|
||||
((aValue & 0x000000ff00000000ULL) >> 8) |
|
||||
((aValue & 0x0000ff0000000000ULL) >> 24) |
|
||||
((aValue & 0x00ff000000000000ULL) >> 40) |
|
||||
((aValue & 0xff00000000000000ULL) >> 56));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
enum Endianness { Little, Big };
|
||||
|
||||
#if MOZ_BIG_ENDIAN
|
||||
# define MOZ_NATIVE_ENDIANNESS detail::Big
|
||||
#else
|
||||
# define MOZ_NATIVE_ENDIANNESS detail::Little
|
||||
#endif
|
||||
|
||||
class EndianUtils
|
||||
{
|
||||
/**
|
||||
* Assert that the memory regions [aDest, aDest+aCount) and
|
||||
* [aSrc, aSrc+aCount] do not overlap. aCount is given in bytes.
|
||||
*/
|
||||
static void assertNoOverlap(const void* aDest, const void* aSrc,
|
||||
size_t aCount)
|
||||
{
|
||||
DebugOnly<const uint8_t*> byteDestPtr = static_cast<const uint8_t*>(aDest);
|
||||
DebugOnly<const uint8_t*> byteSrcPtr = static_cast<const uint8_t*>(aSrc);
|
||||
MOZ_ASSERT((byteDestPtr <= byteSrcPtr &&
|
||||
byteDestPtr + aCount <= byteSrcPtr) ||
|
||||
(byteSrcPtr <= byteDestPtr &&
|
||||
byteSrcPtr + aCount <= byteDestPtr));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void assertAligned(T* aPtr)
|
||||
{
|
||||
MOZ_ASSERT((uintptr_t(aPtr) % sizeof(T)) == 0, "Unaligned pointer!");
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Return |aValue| converted from SourceEndian encoding to DestEndian
|
||||
* encoding.
|
||||
*/
|
||||
template<Endianness SourceEndian, Endianness DestEndian, typename T>
|
||||
static inline T maybeSwap(T aValue)
|
||||
{
|
||||
if (SourceEndian == DestEndian) {
|
||||
return aValue;
|
||||
}
|
||||
return Swapper<T>::swap(aValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert |aCount| elements at |aPtr| from SourceEndian encoding to
|
||||
* DestEndian encoding.
|
||||
*/
|
||||
template<Endianness SourceEndian, Endianness DestEndian, typename T>
|
||||
static inline void maybeSwapInPlace(T* aPtr, size_t aCount)
|
||||
{
|
||||
assertAligned(aPtr);
|
||||
|
||||
if (SourceEndian == DestEndian) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < aCount; i++) {
|
||||
aPtr[i] = Swapper<T>::swap(aPtr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write |aCount| elements to the unaligned address |aDest| in DestEndian
|
||||
* format, using elements found at |aSrc| in SourceEndian format.
|
||||
*/
|
||||
template<Endianness SourceEndian, Endianness DestEndian, typename T>
|
||||
static void copyAndSwapTo(void* aDest, const T* aSrc, size_t aCount)
|
||||
{
|
||||
assertNoOverlap(aDest, aSrc, aCount * sizeof(T));
|
||||
assertAligned(aSrc);
|
||||
|
||||
if (SourceEndian == DestEndian) {
|
||||
memcpy(aDest, aSrc, aCount * sizeof(T));
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* byteDestPtr = static_cast<uint8_t*>(aDest);
|
||||
for (size_t i = 0; i < aCount; ++i) {
|
||||
union
|
||||
{
|
||||
T mVal;
|
||||
uint8_t mBuffer[sizeof(T)];
|
||||
} u;
|
||||
u.mVal = maybeSwap<SourceEndian, DestEndian>(aSrc[i]);
|
||||
memcpy(byteDestPtr, u.mBuffer, sizeof(T));
|
||||
byteDestPtr += sizeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write |aCount| elements to |aDest| in DestEndian format, using elements
|
||||
* found at the unaligned address |aSrc| in SourceEndian format.
|
||||
*/
|
||||
template<Endianness SourceEndian, Endianness DestEndian, typename T>
|
||||
static void copyAndSwapFrom(T* aDest, const void* aSrc, size_t aCount)
|
||||
{
|
||||
assertNoOverlap(aDest, aSrc, aCount * sizeof(T));
|
||||
assertAligned(aDest);
|
||||
|
||||
if (SourceEndian == DestEndian) {
|
||||
memcpy(aDest, aSrc, aCount * sizeof(T));
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t* byteSrcPtr = static_cast<const uint8_t*>(aSrc);
|
||||
for (size_t i = 0; i < aCount; ++i) {
|
||||
union
|
||||
{
|
||||
T mVal;
|
||||
uint8_t mBuffer[sizeof(T)];
|
||||
} u;
|
||||
memcpy(u.mBuffer, byteSrcPtr, sizeof(T));
|
||||
aDest[i] = maybeSwap<SourceEndian, DestEndian>(u.mVal);
|
||||
byteSrcPtr += sizeof(T);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<Endianness ThisEndian>
|
||||
class Endian : private EndianUtils
|
||||
{
|
||||
protected:
|
||||
/** Read a uint16_t in ThisEndian endianness from |aPtr| and return it. */
|
||||
static MOZ_MUST_USE uint16_t readUint16(const void* aPtr)
|
||||
{
|
||||
return read<uint16_t>(aPtr);
|
||||
}
|
||||
|
||||
/** Read a uint32_t in ThisEndian endianness from |aPtr| and return it. */
|
||||
static MOZ_MUST_USE uint32_t readUint32(const void* aPtr)
|
||||
{
|
||||
return read<uint32_t>(aPtr);
|
||||
}
|
||||
|
||||
/** Read a uint64_t in ThisEndian endianness from |aPtr| and return it. */
|
||||
static MOZ_MUST_USE uint64_t readUint64(const void* aPtr)
|
||||
{
|
||||
return read<uint64_t>(aPtr);
|
||||
}
|
||||
|
||||
/** Read an int16_t in ThisEndian endianness from |aPtr| and return it. */
|
||||
static MOZ_MUST_USE int16_t readInt16(const void* aPtr)
|
||||
{
|
||||
return read<int16_t>(aPtr);
|
||||
}
|
||||
|
||||
/** Read an int32_t in ThisEndian endianness from |aPtr| and return it. */
|
||||
static MOZ_MUST_USE int32_t readInt32(const void* aPtr)
|
||||
{
|
||||
return read<uint32_t>(aPtr);
|
||||
}
|
||||
|
||||
/** Read an int64_t in ThisEndian endianness from |aPtr| and return it. */
|
||||
static MOZ_MUST_USE int64_t readInt64(const void* aPtr)
|
||||
{
|
||||
return read<int64_t>(aPtr);
|
||||
}
|
||||
|
||||
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
|
||||
static void writeUint16(void* aPtr, uint16_t aValue)
|
||||
{
|
||||
write(aPtr, aValue);
|
||||
}
|
||||
|
||||
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
|
||||
static void writeUint32(void* aPtr, uint32_t aValue)
|
||||
{
|
||||
write(aPtr, aValue);
|
||||
}
|
||||
|
||||
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
|
||||
static void writeUint64(void* aPtr, uint64_t aValue)
|
||||
{
|
||||
write(aPtr, aValue);
|
||||
}
|
||||
|
||||
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
|
||||
static void writeInt16(void* aPtr, int16_t aValue)
|
||||
{
|
||||
write(aPtr, aValue);
|
||||
}
|
||||
|
||||
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
|
||||
static void writeInt32(void* aPtr, int32_t aValue)
|
||||
{
|
||||
write(aPtr, aValue);
|
||||
}
|
||||
|
||||
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
|
||||
static void writeInt64(void* aPtr, int64_t aValue)
|
||||
{
|
||||
write(aPtr, aValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a value of type T to little-endian format.
|
||||
*
|
||||
* This function is intended for cases where you have data in your
|
||||
* native-endian format and you need it to appear in little-endian
|
||||
* format for transmission.
|
||||
*/
|
||||
template<typename T>
|
||||
MOZ_MUST_USE static T swapToLittleEndian(T aValue)
|
||||
{
|
||||
return maybeSwap<ThisEndian, Little>(aValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
|
||||
* them to little-endian format if ThisEndian is Big.
|
||||
* As with memcpy, |aDest| and |aSrc| must not overlap.
|
||||
*/
|
||||
template<typename T>
|
||||
static void copyAndSwapToLittleEndian(void* aDest, const T* aSrc,
|
||||
size_t aCount)
|
||||
{
|
||||
copyAndSwapTo<ThisEndian, Little>(aDest, aSrc, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise, but converts values in place.
|
||||
*/
|
||||
template<typename T>
|
||||
static void swapToLittleEndianInPlace(T* aPtr, size_t aCount)
|
||||
{
|
||||
maybeSwapInPlace<ThisEndian, Little>(aPtr, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a value of type T to big-endian format.
|
||||
*/
|
||||
template<typename T>
|
||||
MOZ_MUST_USE static T swapToBigEndian(T aValue)
|
||||
{
|
||||
return maybeSwap<ThisEndian, Big>(aValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
|
||||
* them to big-endian format if ThisEndian is Little.
|
||||
* As with memcpy, |aDest| and |aSrc| must not overlap.
|
||||
*/
|
||||
template<typename T>
|
||||
static void copyAndSwapToBigEndian(void* aDest, const T* aSrc,
|
||||
size_t aCount)
|
||||
{
|
||||
copyAndSwapTo<ThisEndian, Big>(aDest, aSrc, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise, but converts values in place.
|
||||
*/
|
||||
template<typename T>
|
||||
static void swapToBigEndianInPlace(T* aPtr, size_t aCount)
|
||||
{
|
||||
maybeSwapInPlace<ThisEndian, Big>(aPtr, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Synonyms for the big-endian functions, for better readability
|
||||
* in network code.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
MOZ_MUST_USE static T swapToNetworkOrder(T aValue)
|
||||
{
|
||||
return swapToBigEndian(aValue);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
copyAndSwapToNetworkOrder(void* aDest, const T* aSrc, size_t aCount)
|
||||
{
|
||||
copyAndSwapToBigEndian(aDest, aSrc, aCount);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
swapToNetworkOrderInPlace(T* aPtr, size_t aCount)
|
||||
{
|
||||
swapToBigEndianInPlace(aPtr, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a value of type T from little-endian format.
|
||||
*/
|
||||
template<typename T>
|
||||
MOZ_MUST_USE static T swapFromLittleEndian(T aValue)
|
||||
{
|
||||
return maybeSwap<Little, ThisEndian>(aValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
|
||||
* them to little-endian format if ThisEndian is Big.
|
||||
* As with memcpy, |aDest| and |aSrc| must not overlap.
|
||||
*/
|
||||
template<typename T>
|
||||
static void copyAndSwapFromLittleEndian(T* aDest, const void* aSrc,
|
||||
size_t aCount)
|
||||
{
|
||||
copyAndSwapFrom<Little, ThisEndian>(aDest, aSrc, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise, but converts values in place.
|
||||
*/
|
||||
template<typename T>
|
||||
static void swapFromLittleEndianInPlace(T* aPtr, size_t aCount)
|
||||
{
|
||||
maybeSwapInPlace<Little, ThisEndian>(aPtr, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a value of type T from big-endian format.
|
||||
*/
|
||||
template<typename T>
|
||||
MOZ_MUST_USE static T swapFromBigEndian(T aValue)
|
||||
{
|
||||
return maybeSwap<Big, ThisEndian>(aValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
|
||||
* them to big-endian format if ThisEndian is Little.
|
||||
* As with memcpy, |aDest| and |aSrc| must not overlap.
|
||||
*/
|
||||
template<typename T>
|
||||
static void copyAndSwapFromBigEndian(T* aDest, const void* aSrc,
|
||||
size_t aCount)
|
||||
{
|
||||
copyAndSwapFrom<Big, ThisEndian>(aDest, aSrc, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise, but converts values in place.
|
||||
*/
|
||||
template<typename T>
|
||||
static void swapFromBigEndianInPlace(T* aPtr, size_t aCount)
|
||||
{
|
||||
maybeSwapInPlace<Big, ThisEndian>(aPtr, aCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Synonyms for the big-endian functions, for better readability
|
||||
* in network code.
|
||||
*/
|
||||
template<typename T>
|
||||
MOZ_MUST_USE static T swapFromNetworkOrder(T aValue)
|
||||
{
|
||||
return swapFromBigEndian(aValue);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void copyAndSwapFromNetworkOrder(T* aDest, const void* aSrc,
|
||||
size_t aCount)
|
||||
{
|
||||
copyAndSwapFromBigEndian(aDest, aSrc, aCount);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void swapFromNetworkOrderInPlace(T* aPtr, size_t aCount)
|
||||
{
|
||||
swapFromBigEndianInPlace(aPtr, aCount);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Read a value of type T, encoded in endianness ThisEndian from |aPtr|.
|
||||
* Return that value encoded in native endianness.
|
||||
*/
|
||||
template<typename T>
|
||||
static T read(const void* aPtr)
|
||||
{
|
||||
union
|
||||
{
|
||||
T mVal;
|
||||
uint8_t mBuffer[sizeof(T)];
|
||||
} u;
|
||||
memcpy(u.mBuffer, aPtr, sizeof(T));
|
||||
return maybeSwap<ThisEndian, MOZ_NATIVE_ENDIANNESS>(u.mVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a value of type T, in native endianness, to |aPtr|, in ThisEndian
|
||||
* endianness.
|
||||
*/
|
||||
template<typename T>
|
||||
static void write(void* aPtr, T aValue)
|
||||
{
|
||||
T tmp = maybeSwap<MOZ_NATIVE_ENDIANNESS, ThisEndian>(aValue);
|
||||
memcpy(aPtr, &tmp, sizeof(T));
|
||||
}
|
||||
|
||||
Endian() = delete;
|
||||
Endian(const Endian& aTther) = delete;
|
||||
void operator=(const Endian& aOther) = delete;
|
||||
};
|
||||
|
||||
template<Endianness ThisEndian>
|
||||
class EndianReadWrite : public Endian<ThisEndian>
|
||||
{
|
||||
private:
|
||||
typedef Endian<ThisEndian> super;
|
||||
|
||||
public:
|
||||
using super::readUint16;
|
||||
using super::readUint32;
|
||||
using super::readUint64;
|
||||
using super::readInt16;
|
||||
using super::readInt32;
|
||||
using super::readInt64;
|
||||
using super::writeUint16;
|
||||
using super::writeUint32;
|
||||
using super::writeUint64;
|
||||
using super::writeInt16;
|
||||
using super::writeInt32;
|
||||
using super::writeInt64;
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
class LittleEndian final : public detail::EndianReadWrite<detail::Little>
|
||||
{};
|
||||
|
||||
class BigEndian final : public detail::EndianReadWrite<detail::Big>
|
||||
{};
|
||||
|
||||
typedef BigEndian NetworkEndian;
|
||||
|
||||
class NativeEndian final : public detail::Endian<MOZ_NATIVE_ENDIANNESS>
|
||||
{
|
||||
private:
|
||||
typedef detail::Endian<MOZ_NATIVE_ENDIANNESS> super;
|
||||
|
||||
public:
|
||||
/*
|
||||
* These functions are intended for cases where you have data in your
|
||||
* native-endian format and you need the data to appear in the appropriate
|
||||
* endianness for transmission, serialization, etc.
|
||||
*/
|
||||
using super::swapToLittleEndian;
|
||||
using super::copyAndSwapToLittleEndian;
|
||||
using super::swapToLittleEndianInPlace;
|
||||
using super::swapToBigEndian;
|
||||
using super::copyAndSwapToBigEndian;
|
||||
using super::swapToBigEndianInPlace;
|
||||
using super::swapToNetworkOrder;
|
||||
using super::copyAndSwapToNetworkOrder;
|
||||
using super::swapToNetworkOrderInPlace;
|
||||
|
||||
/*
|
||||
* These functions are intended for cases where you have data in the
|
||||
* given endianness (e.g. reading from disk or a file-format) and you
|
||||
* need the data to appear in native-endian format for processing.
|
||||
*/
|
||||
using super::swapFromLittleEndian;
|
||||
using super::copyAndSwapFromLittleEndian;
|
||||
using super::swapFromLittleEndianInPlace;
|
||||
using super::swapFromBigEndian;
|
||||
using super::copyAndSwapFromBigEndian;
|
||||
using super::swapFromBigEndianInPlace;
|
||||
using super::swapFromNetworkOrder;
|
||||
using super::copyAndSwapFromNetworkOrder;
|
||||
using super::swapFromNetworkOrderInPlace;
|
||||
};
|
||||
|
||||
#undef MOZ_NATIVE_ENDIANNESS
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_EndianUtils_h */
|
||||
|
|
@ -1,344 +0,0 @@
|
|||
/* -*- 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 set abstraction for enumeration values. */
|
||||
|
||||
#ifndef mozilla_EnumSet_h
|
||||
#define mozilla_EnumSet_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* EnumSet<T> is a set of values defined by an enumeration. It is implemented
|
||||
* using a 32 bit mask for each value so it will only work for enums with an int
|
||||
* representation less than 32. It works both for enum and enum class types.
|
||||
*/
|
||||
template<typename T>
|
||||
class EnumSet
|
||||
{
|
||||
public:
|
||||
EnumSet()
|
||||
: mBitField(0)
|
||||
{
|
||||
initVersion();
|
||||
}
|
||||
|
||||
MOZ_IMPLICIT EnumSet(T aEnum)
|
||||
: mBitField(bitFor(aEnum))
|
||||
{ }
|
||||
|
||||
EnumSet(T aEnum1, T aEnum2)
|
||||
: mBitField(bitFor(aEnum1) |
|
||||
bitFor(aEnum2))
|
||||
{
|
||||
initVersion();
|
||||
}
|
||||
|
||||
EnumSet(T aEnum1, T aEnum2, T aEnum3)
|
||||
: mBitField(bitFor(aEnum1) |
|
||||
bitFor(aEnum2) |
|
||||
bitFor(aEnum3))
|
||||
{
|
||||
initVersion();
|
||||
}
|
||||
|
||||
EnumSet(T aEnum1, T aEnum2, T aEnum3, T aEnum4)
|
||||
: mBitField(bitFor(aEnum1) |
|
||||
bitFor(aEnum2) |
|
||||
bitFor(aEnum3) |
|
||||
bitFor(aEnum4))
|
||||
{
|
||||
initVersion();
|
||||
}
|
||||
|
||||
MOZ_IMPLICIT EnumSet(std::initializer_list<T> list)
|
||||
: mBitField(0)
|
||||
{
|
||||
for (auto value : list) {
|
||||
(*this) += value;
|
||||
}
|
||||
initVersion();
|
||||
}
|
||||
|
||||
EnumSet(const EnumSet& aEnumSet)
|
||||
: mBitField(aEnumSet.mBitField)
|
||||
{
|
||||
initVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element
|
||||
*/
|
||||
void operator+=(T aEnum)
|
||||
{
|
||||
incVersion();
|
||||
mBitField |= bitFor(aEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element
|
||||
*/
|
||||
EnumSet<T> operator+(T aEnum) const
|
||||
{
|
||||
EnumSet<T> result(*this);
|
||||
result += aEnum;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Union
|
||||
*/
|
||||
void operator+=(const EnumSet<T> aEnumSet)
|
||||
{
|
||||
incVersion();
|
||||
mBitField |= aEnumSet.mBitField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Union
|
||||
*/
|
||||
EnumSet<T> operator+(const EnumSet<T> aEnumSet) const
|
||||
{
|
||||
EnumSet<T> result(*this);
|
||||
result += aEnumSet;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an element
|
||||
*/
|
||||
void operator-=(T aEnum)
|
||||
{
|
||||
incVersion();
|
||||
mBitField &= ~(bitFor(aEnum));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an element
|
||||
*/
|
||||
EnumSet<T> operator-(T aEnum) const
|
||||
{
|
||||
EnumSet<T> result(*this);
|
||||
result -= aEnum;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a set of elements
|
||||
*/
|
||||
void operator-=(const EnumSet<T> aEnumSet)
|
||||
{
|
||||
incVersion();
|
||||
mBitField &= ~(aEnumSet.mBitField);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a set of elements
|
||||
*/
|
||||
EnumSet<T> operator-(const EnumSet<T> aEnumSet) const
|
||||
{
|
||||
EnumSet<T> result(*this);
|
||||
result -= aEnumSet;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
incVersion();
|
||||
mBitField = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersection
|
||||
*/
|
||||
void operator&=(const EnumSet<T> aEnumSet)
|
||||
{
|
||||
incVersion();
|
||||
mBitField &= aEnumSet.mBitField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersection
|
||||
*/
|
||||
EnumSet<T> operator&(const EnumSet<T> aEnumSet) const
|
||||
{
|
||||
EnumSet<T> result(*this);
|
||||
result &= aEnumSet;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equality
|
||||
*/
|
||||
bool operator==(const EnumSet<T> aEnumSet) const
|
||||
{
|
||||
return mBitField == aEnumSet.mBitField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test is an element is contained in the set.
|
||||
*/
|
||||
bool contains(T aEnum) const
|
||||
{
|
||||
return mBitField & bitFor(aEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of elements in the set.
|
||||
*/
|
||||
uint8_t size() const
|
||||
{
|
||||
uint8_t count = 0;
|
||||
for (uint32_t bitField = mBitField; bitField; bitField >>= 1) {
|
||||
if (bitField & 1) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return mBitField == 0;
|
||||
}
|
||||
|
||||
uint32_t serialize() const
|
||||
{
|
||||
return mBitField;
|
||||
}
|
||||
|
||||
void deserialize(uint32_t aValue)
|
||||
{
|
||||
incVersion();
|
||||
mBitField = aValue;
|
||||
}
|
||||
|
||||
class ConstIterator
|
||||
{
|
||||
const EnumSet<T>* mSet;
|
||||
uint32_t mPos;
|
||||
#ifdef DEBUG
|
||||
uint64_t mVersion;
|
||||
#endif
|
||||
|
||||
void checkVersion() {
|
||||
// Check that the set has not been modified while being iterated.
|
||||
MOZ_ASSERT_IF(mSet, mSet->mVersion == mVersion);
|
||||
}
|
||||
|
||||
public:
|
||||
ConstIterator(const EnumSet<T>& aSet, uint32_t aPos)
|
||||
: mSet(&aSet), mPos(aPos)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mVersion = mSet->mVersion;
|
||||
#endif
|
||||
MOZ_ASSERT(aPos <= kMaxBits);
|
||||
if (aPos != kMaxBits && !mSet->contains(T(mPos)))
|
||||
++*this;
|
||||
}
|
||||
|
||||
ConstIterator(const ConstIterator& aOther)
|
||||
: mSet(aOther.mSet), mPos(aOther.mPos)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mVersion = aOther.mVersion;
|
||||
checkVersion();
|
||||
#endif
|
||||
}
|
||||
|
||||
ConstIterator(ConstIterator&& aOther)
|
||||
: mSet(aOther.mSet), mPos(aOther.mPos)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mVersion = aOther.mVersion;
|
||||
checkVersion();
|
||||
#endif
|
||||
aOther.mSet = nullptr;
|
||||
}
|
||||
|
||||
~ConstIterator() {
|
||||
checkVersion();
|
||||
}
|
||||
|
||||
bool operator==(const ConstIterator& other) {
|
||||
MOZ_ASSERT(mSet == other.mSet);
|
||||
checkVersion();
|
||||
return mPos == other.mPos;
|
||||
}
|
||||
|
||||
bool operator!=(const ConstIterator& other) {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
T operator*() {
|
||||
MOZ_ASSERT(mSet);
|
||||
MOZ_ASSERT(mPos < kMaxBits);
|
||||
MOZ_ASSERT(mSet->contains(T(mPos)));
|
||||
checkVersion();
|
||||
return T(mPos);
|
||||
}
|
||||
|
||||
ConstIterator& operator++() {
|
||||
MOZ_ASSERT(mSet);
|
||||
MOZ_ASSERT(mPos < kMaxBits);
|
||||
checkVersion();
|
||||
do {
|
||||
mPos++;
|
||||
} while (mPos < kMaxBits && !mSet->contains(T(mPos)));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
ConstIterator begin() const {
|
||||
return ConstIterator(*this, 0);
|
||||
}
|
||||
|
||||
ConstIterator end() const {
|
||||
return ConstIterator(*this, kMaxBits);
|
||||
}
|
||||
|
||||
private:
|
||||
static uint32_t bitFor(T aEnum)
|
||||
{
|
||||
uint32_t bitNumber = (uint32_t)aEnum;
|
||||
MOZ_ASSERT(bitNumber < kMaxBits);
|
||||
return 1U << bitNumber;
|
||||
}
|
||||
|
||||
void initVersion() {
|
||||
#ifdef DEBUG
|
||||
mVersion = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void incVersion() {
|
||||
#ifdef DEBUG
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const size_t kMaxBits = 32;
|
||||
uint32_t mBitField;
|
||||
|
||||
#ifdef DEBUG
|
||||
uint64_t mVersion;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_EnumSet_h_*/
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* Type traits for enums. */
|
||||
|
||||
#ifndef mozilla_EnumTypeTraits_h
|
||||
#define mozilla_EnumTypeTraits_h
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<size_t EnumSize, bool EnumSigned, size_t StorageSize, bool StorageSigned>
|
||||
struct EnumFitsWithinHelper;
|
||||
|
||||
// Signed enum, signed storage.
|
||||
template<size_t EnumSize, size_t StorageSize>
|
||||
struct EnumFitsWithinHelper<EnumSize, true, StorageSize, true>
|
||||
: public std::integral_constant<bool, (EnumSize <= StorageSize)>
|
||||
{};
|
||||
|
||||
// Signed enum, unsigned storage.
|
||||
template<size_t EnumSize, size_t StorageSize>
|
||||
struct EnumFitsWithinHelper<EnumSize, true, StorageSize, false>
|
||||
: public std::integral_constant<bool, false>
|
||||
{};
|
||||
|
||||
// Unsigned enum, signed storage.
|
||||
template<size_t EnumSize, size_t StorageSize>
|
||||
struct EnumFitsWithinHelper<EnumSize, false, StorageSize, true>
|
||||
: public std::integral_constant<bool, (EnumSize * 2 <= StorageSize)>
|
||||
{};
|
||||
|
||||
// Unsigned enum, unsigned storage.
|
||||
template<size_t EnumSize, size_t StorageSize>
|
||||
struct EnumFitsWithinHelper<EnumSize, false, StorageSize, false>
|
||||
: public std::integral_constant<bool, (EnumSize <= StorageSize)>
|
||||
{};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/*
|
||||
* Type trait that determines whether the enum type T can fit within the
|
||||
* integral type Storage without data loss. This trait should be used with
|
||||
* caution with an enum type whose underlying type has not been explicitly
|
||||
* specified: for such enums, the C++ implementation is free to choose a type
|
||||
* no smaller than int whose range encompasses all possible values of the enum.
|
||||
* So for an enum with only small non-negative values, the underlying type may
|
||||
* be either int or unsigned int, depending on the whims of the implementation.
|
||||
*/
|
||||
template<typename T, typename Storage>
|
||||
struct EnumTypeFitsWithin
|
||||
: public detail::EnumFitsWithinHelper<
|
||||
sizeof(T),
|
||||
std::is_signed<typename std::underlying_type<T>::type>::value,
|
||||
sizeof(Storage),
|
||||
std::is_signed<Storage>::value
|
||||
>
|
||||
{
|
||||
static_assert(std::is_enum<T>::value, "must provide an enum type");
|
||||
static_assert(std::is_integral<Storage>::value, "must provide an integral type");
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_EnumTypeTraits_h */
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* EnumeratedArray is like Array, but indexed by a typed enum. */
|
||||
|
||||
#ifndef mozilla_EnumeratedArray_h
|
||||
#define mozilla_EnumeratedArray_h
|
||||
|
||||
#include "mozilla/Array.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* EnumeratedArray is a fixed-size array container for use when an
|
||||
* array is indexed by a specific enum class.
|
||||
*
|
||||
* This provides type safety by guarding at compile time against accidentally
|
||||
* indexing such arrays with unrelated values. This also removes the need
|
||||
* for manual casting when using a typed enum value to index arrays.
|
||||
*
|
||||
* Aside from the typing of indices, EnumeratedArray is similar to Array.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* enum class AnimalSpecies {
|
||||
* Cow,
|
||||
* Sheep,
|
||||
* Count
|
||||
* };
|
||||
*
|
||||
* EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount;
|
||||
*
|
||||
* headCount[AnimalSpecies::Cow] = 17;
|
||||
* headCount[AnimalSpecies::Sheep] = 30;
|
||||
*
|
||||
*/
|
||||
template<typename IndexType,
|
||||
IndexType SizeAsEnumValue,
|
||||
typename ValueType>
|
||||
class EnumeratedArray
|
||||
{
|
||||
public:
|
||||
static const size_t kSize = size_t(SizeAsEnumValue);
|
||||
|
||||
private:
|
||||
typedef Array<ValueType, kSize> ArrayType;
|
||||
|
||||
ArrayType mArray;
|
||||
|
||||
public:
|
||||
EnumeratedArray() {}
|
||||
|
||||
template <typename... Args>
|
||||
MOZ_IMPLICIT EnumeratedArray(Args&&... aArgs)
|
||||
: mArray{mozilla::Forward<Args>(aArgs)...}
|
||||
{}
|
||||
|
||||
explicit EnumeratedArray(const EnumeratedArray& aOther)
|
||||
{
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
mArray[i] = aOther.mArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
EnumeratedArray(EnumeratedArray&& aOther)
|
||||
{
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
mArray[i] = Move(aOther.mArray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ValueType& operator[](IndexType aIndex)
|
||||
{
|
||||
return mArray[size_t(aIndex)];
|
||||
}
|
||||
|
||||
const ValueType& operator[](IndexType aIndex) const
|
||||
{
|
||||
return mArray[size_t(aIndex)];
|
||||
}
|
||||
|
||||
typedef typename ArrayType::iterator iterator;
|
||||
typedef typename ArrayType::const_iterator const_iterator;
|
||||
typedef typename ArrayType::reverse_iterator reverse_iterator;
|
||||
typedef typename ArrayType::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
// Methods for range-based for loops.
|
||||
iterator begin() { return mArray.begin(); }
|
||||
const_iterator begin() const { return mArray.begin(); }
|
||||
const_iterator cbegin() const { return mArray.cbegin(); }
|
||||
iterator end() { return mArray.end(); }
|
||||
const_iterator end() const { return mArray.end(); }
|
||||
const_iterator cend() const { return mArray.cend(); }
|
||||
|
||||
// Methods for reverse iterating.
|
||||
reverse_iterator rbegin() { return mArray.rbegin(); }
|
||||
const_reverse_iterator rbegin() const { return mArray.rbegin(); }
|
||||
const_reverse_iterator crbegin() const { return mArray.crbegin(); }
|
||||
reverse_iterator rend() { return mArray.rend(); }
|
||||
const_reverse_iterator rend() const { return mArray.rend(); }
|
||||
const_reverse_iterator crend() const { return mArray.crend(); }
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_EnumeratedArray_h
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* Iterator over contiguous enum values */
|
||||
|
||||
/*
|
||||
* Implements generator functions that create a range to iterate over the values
|
||||
* of a scoped or unscoped enum. Unlike IntegerRange, which can only function on
|
||||
* the underlying integral type, the elements of the generated sequence will
|
||||
* have the type of the enum in question.
|
||||
*
|
||||
* Note that the enum values should be contiguous in the iterated range;
|
||||
* unfortunately there exists no way for EnumeratedRange to enforce this
|
||||
* either dynamically or at compile time.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_EnumeratedRange_h
|
||||
#define mozilla_EnumeratedRange_h
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "mozilla/ReverseIterator.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename EnumTypeT>
|
||||
class EnumeratedIterator
|
||||
{
|
||||
public:
|
||||
typedef typename std::underlying_type<EnumTypeT>::type IntTypeT;
|
||||
|
||||
template<typename EnumType>
|
||||
explicit EnumeratedIterator(EnumType aCurrent)
|
||||
: mCurrent(aCurrent) { }
|
||||
|
||||
template<typename EnumType>
|
||||
explicit EnumeratedIterator(const EnumeratedIterator<EnumType>& aOther)
|
||||
: mCurrent(aOther.mCurrent) { }
|
||||
|
||||
EnumTypeT operator*() const { return mCurrent; }
|
||||
|
||||
/* Increment and decrement operators */
|
||||
|
||||
EnumeratedIterator& operator++()
|
||||
{
|
||||
mCurrent = EnumTypeT(IntTypeT(mCurrent) + IntTypeT(1));
|
||||
return *this;
|
||||
}
|
||||
EnumeratedIterator& operator--()
|
||||
{
|
||||
mCurrent = EnumTypeT(IntTypeT(mCurrent) - IntTypeT(1));
|
||||
return *this;
|
||||
}
|
||||
EnumeratedIterator operator++(int)
|
||||
{
|
||||
auto ret = *this;
|
||||
mCurrent = EnumTypeT(IntTypeT(mCurrent) + IntTypeT(1));
|
||||
return ret;
|
||||
}
|
||||
EnumeratedIterator operator--(int)
|
||||
{
|
||||
auto ret = *this;
|
||||
mCurrent = EnumTypeT(IntTypeT(mCurrent) - IntTypeT(1));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Comparison operators */
|
||||
|
||||
template<typename EnumType>
|
||||
friend bool operator==(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2);
|
||||
template<typename EnumType>
|
||||
friend bool operator!=(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2);
|
||||
template<typename EnumType>
|
||||
friend bool operator<(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2);
|
||||
template<typename EnumType>
|
||||
friend bool operator<=(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2);
|
||||
template<typename EnumType>
|
||||
friend bool operator>(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2);
|
||||
template<typename EnumType>
|
||||
friend bool operator>=(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2);
|
||||
|
||||
private:
|
||||
EnumTypeT mCurrent;
|
||||
};
|
||||
|
||||
template<typename EnumType>
|
||||
bool operator==(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent == aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename EnumType>
|
||||
bool operator!=(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent != aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename EnumType>
|
||||
bool operator<(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent < aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename EnumType>
|
||||
bool operator<=(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent <= aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename EnumType>
|
||||
bool operator>(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent > aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename EnumType>
|
||||
bool operator>=(const EnumeratedIterator<EnumType>& aIter1,
|
||||
const EnumeratedIterator<EnumType>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent >= aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename EnumTypeT>
|
||||
class EnumeratedRange
|
||||
{
|
||||
public:
|
||||
typedef EnumeratedIterator<EnumTypeT> iterator;
|
||||
typedef EnumeratedIterator<EnumTypeT> const_iterator;
|
||||
typedef ReverseIterator<iterator> reverse_iterator;
|
||||
typedef ReverseIterator<const_iterator> const_reverse_iterator;
|
||||
|
||||
template<typename EnumType>
|
||||
EnumeratedRange(EnumType aBegin, EnumType aEnd)
|
||||
: mBegin(aBegin), mEnd(aEnd) { }
|
||||
|
||||
iterator begin() const { return iterator(mBegin); }
|
||||
const_iterator cbegin() const { return begin(); }
|
||||
iterator end() const { return iterator(mEnd); }
|
||||
const_iterator cend() const { return end(); }
|
||||
reverse_iterator rbegin() const { return reverse_iterator(mEnd); }
|
||||
const_reverse_iterator crbegin() const { return rbegin(); }
|
||||
reverse_iterator rend() const { return reverse_iterator(mBegin); }
|
||||
const_reverse_iterator crend() const { return rend(); }
|
||||
|
||||
private:
|
||||
EnumTypeT mBegin;
|
||||
EnumTypeT mEnd;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#ifdef __GNUC__
|
||||
// Enums can have an unsigned underlying type, which makes some of the
|
||||
// comparisons below always true or always false. Temporarily disable
|
||||
// -Wtype-limits to avoid breaking -Werror builds.
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#endif
|
||||
|
||||
// Create a range to iterate from aBegin to aEnd, exclusive.
|
||||
template<typename EnumType>
|
||||
inline detail::EnumeratedRange<EnumType>
|
||||
MakeEnumeratedRange(EnumType aBegin, EnumType aEnd)
|
||||
{
|
||||
MOZ_ASSERT(aBegin <= aEnd, "Cannot generate invalid, unbounded range!");
|
||||
return detail::EnumeratedRange<EnumType>(aBegin, aEnd);
|
||||
}
|
||||
|
||||
// Create a range to iterate from EnumType(0) to aEnd, exclusive. EnumType(0)
|
||||
// should exist, but note that there is no way for us to ensure that it does!
|
||||
template<typename EnumType>
|
||||
inline detail::EnumeratedRange<EnumType>
|
||||
MakeEnumeratedRange(EnumType aEnd)
|
||||
{
|
||||
return MakeEnumeratedRange(EnumType(0), aEnd);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_EnumeratedRange_h
|
||||
|
||||
|
|
@ -1,379 +0,0 @@
|
|||
/* -*- 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_FastBernoulliTrial_h
|
||||
#define mozilla_FastBernoulliTrial_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/XorShift128PlusRNG.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* class FastBernoulliTrial: Efficient sampling with uniform probability
|
||||
*
|
||||
* When gathering statistics about a program's behavior, we may be observing
|
||||
* events that occur very frequently (e.g., function calls or memory
|
||||
* allocations) and we may be gathering information that is somewhat expensive
|
||||
* to produce (e.g., call stacks). Sampling all the events could have a
|
||||
* significant impact on the program's performance.
|
||||
*
|
||||
* Why not just sample every N'th event? This technique is called "systematic
|
||||
* sampling"; it's simple and efficient, and it's fine if we imagine a
|
||||
* patternless stream of events. But what if we're sampling allocations, and the
|
||||
* program happens to have a loop where each iteration does exactly N
|
||||
* allocations? You would end up sampling the same allocation every time through
|
||||
* the loop; the entire rest of the loop becomes invisible to your measurements!
|
||||
* More generally, if each iteration does M allocations, and M and N have any
|
||||
* common divisor at all, most allocation sites will never be sampled. If
|
||||
* they're both even, say, the odd-numbered allocations disappear from your
|
||||
* results.
|
||||
*
|
||||
* Ideally, we'd like each event to have some probability P of being sampled,
|
||||
* independent of its neighbors and of its position in the sequence. This is
|
||||
* called "Bernoulli sampling", and it doesn't suffer from any of the problems
|
||||
* mentioned above.
|
||||
*
|
||||
* One disadvantage of Bernoulli sampling is that you can't be sure exactly how
|
||||
* many samples you'll get: technically, it's possible that you might sample
|
||||
* none of them, or all of them. But if the number of events N is large, these
|
||||
* aren't likely outcomes; you can generally expect somewhere around P * N
|
||||
* events to be sampled.
|
||||
*
|
||||
* The other disadvantage of Bernoulli sampling is that you have to generate a
|
||||
* random number for every event, which can be slow.
|
||||
*
|
||||
* [significant pause]
|
||||
*
|
||||
* BUT NOT WITH THIS CLASS! FastBernoulliTrial lets you do true Bernoulli
|
||||
* sampling, while generating a fresh random number only when we do decide to
|
||||
* sample an event, not on every trial. When it decides not to sample, a call to
|
||||
* |FastBernoulliTrial::trial| is nothing but decrementing a counter and
|
||||
* comparing it to zero. So the lower your sampling probability is, the less
|
||||
* overhead FastBernoulliTrial imposes.
|
||||
*
|
||||
* Probabilities of 0 and 1 are handled efficiently. (In neither case need we
|
||||
* ever generate a random number at all.)
|
||||
*
|
||||
* The essential API:
|
||||
*
|
||||
* - FastBernoulliTrial(double P)
|
||||
* Construct an instance that selects events with probability P.
|
||||
*
|
||||
* - FastBernoulliTrial::trial()
|
||||
* Return true with probability P. Call this each time an event occurs, to
|
||||
* decide whether to sample it or not.
|
||||
*
|
||||
* - FastBernoulliTrial::trial(size_t n)
|
||||
* Equivalent to calling trial() |n| times, and returning true if any of those
|
||||
* calls do. However, like trial, this runs in fast constant time.
|
||||
*
|
||||
* What is this good for? In some applications, some events are "bigger" than
|
||||
* others. For example, large allocations are more significant than small
|
||||
* allocations. Perhaps we'd like to imagine that we're drawing allocations
|
||||
* from a stream of bytes, and performing a separate Bernoulli trial on every
|
||||
* byte from the stream. We can accomplish this by calling |t.trial(S)| for
|
||||
* the number of bytes S, and sampling the event if that returns true.
|
||||
*
|
||||
* Of course, this style of sampling needs to be paired with analysis and
|
||||
* presentation that makes the size of the event apparent, lest trials with
|
||||
* large values for |n| appear to be indistinguishable from those with small
|
||||
* values for |n|.
|
||||
*/
|
||||
class FastBernoulliTrial {
|
||||
/*
|
||||
* This comment should just read, "Generate skip counts with a geometric
|
||||
* distribution", and leave everyone to go look that up and see why it's the
|
||||
* right thing to do, if they don't know already.
|
||||
*
|
||||
* BUT IF YOU'RE CURIOUS, COMMENTS ARE FREE...
|
||||
*
|
||||
* Instead of generating a fresh random number for every trial, we can
|
||||
* randomly generate a count of how many times we should return false before
|
||||
* the next time we return true. We call this a "skip count". Once we've
|
||||
* returned true, we generate a fresh skip count, and begin counting down
|
||||
* again.
|
||||
*
|
||||
* Here's an awesome fact: by exercising a little care in the way we generate
|
||||
* skip counts, we can produce results indistinguishable from those we would
|
||||
* get "rolling the dice" afresh for every trial.
|
||||
*
|
||||
* In short, skip counts in Bernoulli trials of probability P obey a geometric
|
||||
* distribution. If a random variable X is uniformly distributed from [0..1),
|
||||
* then std::floor(std::log(X) / std::log(1-P)) has the appropriate geometric
|
||||
* distribution for the skip counts.
|
||||
*
|
||||
* Why that formula?
|
||||
*
|
||||
* Suppose we're to return |true| with some probability P, say, 0.3. Spread
|
||||
* all possible futures along a line segment of length 1. In portion P of
|
||||
* those cases, we'll return true on the next call to |trial|; the skip count
|
||||
* is 0. For the remaining portion 1-P of cases, the skip count is 1 or more.
|
||||
*
|
||||
* skip: 0 1 or more
|
||||
* |------------------^-----------------------------------------|
|
||||
* portion: 0.3 0.7
|
||||
* P 1-P
|
||||
*
|
||||
* But the "1 or more" section of the line is subdivided the same way: *within
|
||||
* that section*, in portion P the second call to |trial()| returns true, and in
|
||||
* portion 1-P it returns false a second time; the skip count is two or more.
|
||||
* So we return true on the second call in proportion 0.7 * 0.3, and skip at
|
||||
* least the first two in proportion 0.7 * 0.7.
|
||||
*
|
||||
* skip: 0 1 2 or more
|
||||
* |------------------^------------^----------------------------|
|
||||
* portion: 0.3 0.7 * 0.3 0.7 * 0.7
|
||||
* P (1-P)*P (1-P)^2
|
||||
*
|
||||
* We can continue to subdivide:
|
||||
*
|
||||
* skip >= 0: |------------------------------------------------- (1-P)^0 --|
|
||||
* skip >= 1: | ------------------------------- (1-P)^1 --|
|
||||
* skip >= 2: | ------------------ (1-P)^2 --|
|
||||
* skip >= 3: | ^ ---------- (1-P)^3 --|
|
||||
* skip >= 4: | . --- (1-P)^4 --|
|
||||
* .
|
||||
* ^X, see below
|
||||
*
|
||||
* In other words, the likelihood of the next n calls to |trial| returning
|
||||
* false is (1-P)^n. The longer a run we require, the more the likelihood
|
||||
* drops. Further calls may return false too, but this is the probability
|
||||
* we'll skip at least n.
|
||||
*
|
||||
* This is interesting, because we can pick a point along this line segment
|
||||
* and see which skip count's range it falls within; the point X above, for
|
||||
* example, is within the ">= 2" range, but not within the ">= 3" range, so it
|
||||
* designates a skip count of 2. So if we pick points on the line at random
|
||||
* and use the skip counts they fall under, that will be indistinguishable
|
||||
* from generating a fresh random number between 0 and 1 for each trial and
|
||||
* comparing it to P.
|
||||
*
|
||||
* So to find the skip count for a point X, we must ask: To what whole power
|
||||
* must we raise 1-P such that we include X, but the next power would exclude
|
||||
* it? This is exactly std::floor(std::log(X) / std::log(1-P)).
|
||||
*
|
||||
* Our algorithm is then, simply: When constructed, compute an initial skip
|
||||
* count. Return false from |trial| that many times, and then compute a new skip
|
||||
* count.
|
||||
*
|
||||
* For a call to |trial(n)|, if the skip count is greater than n, return false
|
||||
* and subtract n from the skip count. If the skip count is less than n,
|
||||
* return true and compute a new skip count. Since each trial is independent,
|
||||
* it doesn't matter by how much n overshoots the skip count; we can actually
|
||||
* compute a new skip count at *any* time without affecting the distribution.
|
||||
* This is really beautiful.
|
||||
*/
|
||||
public:
|
||||
/**
|
||||
* Construct a fast Bernoulli trial generator. Calls to |trial()| return true
|
||||
* with probability |aProbability|. Use |aState0| and |aState1| to seed the
|
||||
* random number generator; both may not be zero.
|
||||
*/
|
||||
FastBernoulliTrial(double aProbability, uint64_t aState0, uint64_t aState1)
|
||||
: mProbability(0)
|
||||
, mInvLogNotProbability(0)
|
||||
, mGenerator(aState0, aState1)
|
||||
, mSkipCount(0)
|
||||
{
|
||||
setProbability(aProbability);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true with probability |mProbability|. Call this each time an event
|
||||
* occurs, to decide whether to sample it or not. The lower |mProbability| is,
|
||||
* the faster this function runs.
|
||||
*/
|
||||
bool trial() {
|
||||
if (mSkipCount) {
|
||||
mSkipCount--;
|
||||
return false;
|
||||
}
|
||||
|
||||
return chooseSkipCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to calling trial() |n| times, and returning true if any of those
|
||||
* calls do. However, like trial, this runs in fast constant time.
|
||||
*
|
||||
* What is this good for? In some applications, some events are "bigger" than
|
||||
* others. For example, large allocations are more significant than small
|
||||
* allocations. Perhaps we'd like to imagine that we're drawing allocations
|
||||
* from a stream of bytes, and performing a separate Bernoulli trial on every
|
||||
* byte from the stream. We can accomplish this by calling |t.trial(S)| for
|
||||
* the number of bytes S, and sampling the event if that returns true.
|
||||
*
|
||||
* Of course, this style of sampling needs to be paired with analysis and
|
||||
* presentation that makes the "size" of the event apparent, lest trials with
|
||||
* large values for |n| appear to be indistinguishable from those with small
|
||||
* values for |n|, despite being potentially much more likely to be sampled.
|
||||
*/
|
||||
bool trial(size_t aCount) {
|
||||
if (mSkipCount > aCount) {
|
||||
mSkipCount -= aCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
return chooseSkipCount();
|
||||
}
|
||||
|
||||
void setRandomState(uint64_t aState0, uint64_t aState1) {
|
||||
mGenerator.setState(aState0, aState1);
|
||||
}
|
||||
|
||||
void setProbability(double aProbability) {
|
||||
MOZ_ASSERT(0 <= aProbability && aProbability <= 1);
|
||||
mProbability = aProbability;
|
||||
if (0 < mProbability && mProbability < 1) {
|
||||
/*
|
||||
* Let's look carefully at how this calculation plays out in floating-
|
||||
* point arithmetic. We'll assume IEEE, but the final C++ code we arrive
|
||||
* at would still be fine if our numbers were mathematically perfect. So,
|
||||
* while we've considered IEEE's edge cases, we haven't done anything that
|
||||
* should be actively bad when using other representations.
|
||||
*
|
||||
* (In the below, read comparisons as exact mathematical comparisons: when
|
||||
* we say something "equals 1", that means it's exactly equal to 1. We
|
||||
* treat approximation using intervals with open boundaries: saying a
|
||||
* value is in (0,1) doesn't specify how close to 0 or 1 the value gets.
|
||||
* When we use closed boundaries like [2**-53, 1], we're careful to ensure
|
||||
* the boundary values are actually representable.)
|
||||
*
|
||||
* - After the comparison above, we know mProbability is in (0,1).
|
||||
*
|
||||
* - The gaps below 1 are 2**-53, so that interval is (0, 1-2**-53].
|
||||
*
|
||||
* - Because the floating-point gaps near 1 are wider than those near
|
||||
* zero, there are many small positive doubles ε such that 1-ε rounds to
|
||||
* exactly 1. However, 2**-53 can be represented exactly. So
|
||||
* 1-mProbability is in [2**-53, 1].
|
||||
*
|
||||
* - log(1 - mProbability) is thus in (-37, 0].
|
||||
*
|
||||
* That range includes zero, but when we use mInvLogNotProbability, it
|
||||
* would be helpful if we could trust that it's negative. So when log(1
|
||||
* - mProbability) is 0, we'll just set mProbability to 0, so that
|
||||
* mInvLogNotProbability is not used in chooseSkipCount.
|
||||
*
|
||||
* - How much of the range of mProbability does this cause us to ignore?
|
||||
* The only value for which log returns 0 is exactly 1; the slope of log
|
||||
* at 1 is 1, so for small ε such that 1 - ε != 1, log(1 - ε) is -ε,
|
||||
* never 0. The gaps near one are larger than the gaps near zero, so if
|
||||
* 1 - ε wasn't 1, then -ε is representable. So if log(1 - mProbability)
|
||||
* isn't 0, then 1 - mProbability isn't 1, which means that mProbability
|
||||
* is at least 2**-53, as discussed earlier. This is a sampling
|
||||
* likelihood of roughly one in ten trillion, which is unlikely to be
|
||||
* distinguishable from zero in practice.
|
||||
*
|
||||
* So by forbidding zero, we've tightened our range to (-37, -2**-53].
|
||||
*
|
||||
* - Finally, 1 / log(1 - mProbability) is in [-2**53, -1/37). This all
|
||||
* falls readily within the range of an IEEE double.
|
||||
*
|
||||
* ALL THAT HAVING BEEN SAID: here are the five lines of actual code:
|
||||
*/
|
||||
double logNotProbability = std::log(1 - mProbability);
|
||||
if (logNotProbability == 0.0)
|
||||
mProbability = 0.0;
|
||||
else
|
||||
mInvLogNotProbability = 1 / logNotProbability;
|
||||
}
|
||||
|
||||
chooseSkipCount();
|
||||
}
|
||||
|
||||
private:
|
||||
/* The likelihood that any given call to |trial| should return true. */
|
||||
double mProbability;
|
||||
|
||||
/*
|
||||
* The value of 1/std::log(1 - mProbability), cached for repeated use.
|
||||
*
|
||||
* If mProbability is exactly 0 or exactly 1, we don't use this value.
|
||||
* Otherwise, we guarantee this value is in the range [-2**53, -1/37), i.e.
|
||||
* definitely negative, as required by chooseSkipCount. See setProbability for
|
||||
* the details.
|
||||
*/
|
||||
double mInvLogNotProbability;
|
||||
|
||||
/* Our random number generator. */
|
||||
non_crypto::XorShift128PlusRNG mGenerator;
|
||||
|
||||
/* The number of times |trial| should return false before next returning true. */
|
||||
size_t mSkipCount;
|
||||
|
||||
/*
|
||||
* Choose the next skip count. This also returns the value that |trial| should
|
||||
* return, since we have to check for the extreme values for mProbability
|
||||
* anyway, and |trial| should never return true at all when mProbability is 0.
|
||||
*/
|
||||
bool chooseSkipCount() {
|
||||
/*
|
||||
* If the probability is 1.0, every call to |trial| returns true. Make sure
|
||||
* mSkipCount is 0.
|
||||
*/
|
||||
if (mProbability == 1.0) {
|
||||
mSkipCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the probabilility is zero, |trial| never returns true. Don't bother us
|
||||
* for a while.
|
||||
*/
|
||||
if (mProbability == 0.0) {
|
||||
mSkipCount = SIZE_MAX;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* What sorts of values can this call to std::floor produce?
|
||||
*
|
||||
* Since mGenerator.nextDouble returns a value in [0, 1-2**-53], std::log
|
||||
* returns a value in the range [-infinity, -2**-53], all negative. Since
|
||||
* mInvLogNotProbability is negative (see its comments), the product is
|
||||
* positive and possibly infinite. std::floor returns +infinity unchanged.
|
||||
* So the result will always be positive.
|
||||
*
|
||||
* Converting a double to an integer that is out of range for that integer
|
||||
* is undefined behavior, so we must clamp our result to SIZE_MAX, to ensure
|
||||
* we get an acceptable value for mSkipCount.
|
||||
*
|
||||
* The clamp is written carefully. Note that if we had said:
|
||||
*
|
||||
* if (skipCount > SIZE_MAX)
|
||||
* skipCount = SIZE_MAX;
|
||||
*
|
||||
* that leads to undefined behavior 64-bit machines: SIZE_MAX coerced to
|
||||
* double is 2^64, not 2^64-1, so this doesn't actually set skipCount to a
|
||||
* value that can be safely assigned to mSkipCount.
|
||||
*
|
||||
* Jakub Oleson cleverly suggested flipping the sense of the comparison: if
|
||||
* we require that skipCount < SIZE_MAX, then because of the gaps (2048)
|
||||
* between doubles at that magnitude, the highest double less than 2^64 is
|
||||
* 2^64 - 2048, which is fine to store in a size_t.
|
||||
*
|
||||
* (On 32-bit machines, all size_t values can be represented exactly in
|
||||
* double, so all is well.)
|
||||
*/
|
||||
double skipCount = std::floor(std::log(mGenerator.nextDouble())
|
||||
* mInvLogNotProbability);
|
||||
if (skipCount < SIZE_MAX)
|
||||
mSkipCount = skipCount;
|
||||
else
|
||||
mSkipCount = SIZE_MAX;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_FastBernoulliTrial_h */
|
||||
|
|
@ -1,479 +0,0 @@
|
|||
/* -*- 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 predicates and operations on IEEE-754 floating point types. */
|
||||
|
||||
#ifndef mozilla_FloatingPoint_h
|
||||
#define mozilla_FloatingPoint_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* It's reasonable to ask why we have this header at all. Don't isnan,
|
||||
* copysign, the built-in comparison operators, and the like solve these
|
||||
* problems? Unfortunately, they don't. We've found that various compilers
|
||||
* (MSVC, MSVC when compiling with PGO, and GCC on OS X, at least) miscompile
|
||||
* the standard methods in various situations, so we can't use them. Some of
|
||||
* these compilers even have problems compiling seemingly reasonable bitwise
|
||||
* algorithms! But with some care we've found algorithms that seem to not
|
||||
* trigger those compiler bugs.
|
||||
*
|
||||
* For the aforementioned reasons, be very wary of making changes to any of
|
||||
* these algorithms. If you must make changes, keep a careful eye out for
|
||||
* compiler bustage, particularly PGO-specific bustage.
|
||||
*/
|
||||
|
||||
struct FloatTypeTraits
|
||||
{
|
||||
typedef uint32_t Bits;
|
||||
|
||||
static const unsigned kExponentBias = 127;
|
||||
static const unsigned kExponentShift = 23;
|
||||
|
||||
static const Bits kSignBit = 0x80000000UL;
|
||||
static const Bits kExponentBits = 0x7F800000UL;
|
||||
static const Bits kSignificandBits = 0x007FFFFFUL;
|
||||
};
|
||||
|
||||
struct DoubleTypeTraits
|
||||
{
|
||||
typedef uint64_t Bits;
|
||||
|
||||
static const unsigned kExponentBias = 1023;
|
||||
static const unsigned kExponentShift = 52;
|
||||
|
||||
static const Bits kSignBit = 0x8000000000000000ULL;
|
||||
static const Bits kExponentBits = 0x7ff0000000000000ULL;
|
||||
static const Bits kSignificandBits = 0x000fffffffffffffULL;
|
||||
};
|
||||
|
||||
template<typename T> struct SelectTrait;
|
||||
template<> struct SelectTrait<float> : public FloatTypeTraits {};
|
||||
template<> struct SelectTrait<double> : public DoubleTypeTraits {};
|
||||
|
||||
/*
|
||||
* This struct contains details regarding the encoding of floating-point
|
||||
* numbers that can be useful for direct bit manipulation. As of now, the
|
||||
* template parameter has to be float or double.
|
||||
*
|
||||
* The nested typedef |Bits| is the unsigned integral type with the same size
|
||||
* as T: uint32_t for float and uint64_t for double (static assertions
|
||||
* double-check these assumptions).
|
||||
*
|
||||
* kExponentBias is the offset that is subtracted from the exponent when
|
||||
* computing the value, i.e. one plus the opposite of the mininum possible
|
||||
* exponent.
|
||||
* kExponentShift is the shift that one needs to apply to retrieve the
|
||||
* exponent component of the value.
|
||||
*
|
||||
* kSignBit contains a bits mask. Bit-and-ing with this mask will result in
|
||||
* obtaining the sign bit.
|
||||
* kExponentBits contains the mask needed for obtaining the exponent bits and
|
||||
* kSignificandBits contains the mask needed for obtaining the significand
|
||||
* bits.
|
||||
*
|
||||
* Full details of how floating point number formats are encoded are beyond
|
||||
* the scope of this comment. For more information, see
|
||||
* http://en.wikipedia.org/wiki/IEEE_floating_point
|
||||
* http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers
|
||||
*/
|
||||
template<typename T>
|
||||
struct FloatingPoint : public SelectTrait<T>
|
||||
{
|
||||
typedef SelectTrait<T> Base;
|
||||
typedef typename Base::Bits Bits;
|
||||
|
||||
static_assert((Base::kSignBit & Base::kExponentBits) == 0,
|
||||
"sign bit shouldn't overlap exponent bits");
|
||||
static_assert((Base::kSignBit & Base::kSignificandBits) == 0,
|
||||
"sign bit shouldn't overlap significand bits");
|
||||
static_assert((Base::kExponentBits & Base::kSignificandBits) == 0,
|
||||
"exponent bits shouldn't overlap significand bits");
|
||||
|
||||
static_assert((Base::kSignBit | Base::kExponentBits | Base::kSignificandBits) ==
|
||||
~Bits(0),
|
||||
"all bits accounted for");
|
||||
|
||||
/*
|
||||
* These implementations assume float/double are 32/64-bit single/double
|
||||
* format number types compatible with the IEEE-754 standard. C++ don't
|
||||
* require this to be the case. But we required this in implementations of
|
||||
* these algorithms that preceded this header, so we shouldn't break anything
|
||||
* if we keep doing so.
|
||||
*/
|
||||
static_assert(sizeof(T) == sizeof(Bits), "Bits must be same size as T");
|
||||
};
|
||||
|
||||
/** Determines whether a float/double is NaN. */
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsNaN(T aValue)
|
||||
{
|
||||
/*
|
||||
* A float/double is NaN if all exponent bits are 1 and the significand
|
||||
* contains at least one non-zero bit.
|
||||
*/
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
return (BitwiseCast<Bits>(aValue) & Traits::kExponentBits) == Traits::kExponentBits &&
|
||||
(BitwiseCast<Bits>(aValue) & Traits::kSignificandBits) != 0;
|
||||
}
|
||||
|
||||
/** Determines whether a float/double is +Infinity or -Infinity. */
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsInfinite(T aValue)
|
||||
{
|
||||
/* Infinities have all exponent bits set to 1 and an all-0 significand. */
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
Bits bits = BitwiseCast<Bits>(aValue);
|
||||
return (bits & ~Traits::kSignBit) == Traits::kExponentBits;
|
||||
}
|
||||
|
||||
/** Determines whether a float/double is not NaN or infinite. */
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsFinite(T aValue)
|
||||
{
|
||||
/*
|
||||
* NaN and Infinities are the only non-finite floats/doubles, and both have
|
||||
* all exponent bits set to 1.
|
||||
*/
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
Bits bits = BitwiseCast<Bits>(aValue);
|
||||
return (bits & Traits::kExponentBits) != Traits::kExponentBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a float/double is negative or -0. It is an error
|
||||
* to call this method on a float/double which is NaN.
|
||||
*/
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsNegative(T aValue)
|
||||
{
|
||||
MOZ_ASSERT(!IsNaN(aValue), "NaN does not have a sign");
|
||||
|
||||
/* The sign bit is set if the double is negative. */
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
Bits bits = BitwiseCast<Bits>(aValue);
|
||||
return (bits & Traits::kSignBit) != 0;
|
||||
}
|
||||
|
||||
/** Determines whether a float/double represents -0. */
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsNegativeZero(T aValue)
|
||||
{
|
||||
/* Only the sign bit is set if the value is -0. */
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
Bits bits = BitwiseCast<Bits>(aValue);
|
||||
return bits == Traits::kSignBit;
|
||||
}
|
||||
|
||||
/** Determines wether a float/double represents +0. */
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsPositiveZero(T aValue)
|
||||
{
|
||||
/* All bits are zero if the value is +0. */
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
Bits bits = BitwiseCast<Bits>(aValue);
|
||||
return bits == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 if a float/double is NaN or infinite;
|
||||
* otherwise, the float/double is returned.
|
||||
*/
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE T
|
||||
ToZeroIfNonfinite(T aValue)
|
||||
{
|
||||
return IsFinite(aValue) ? aValue : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exponent portion of the float/double.
|
||||
*
|
||||
* Zero is not special-cased, so ExponentComponent(0.0) is
|
||||
* -int_fast16_t(Traits::kExponentBias).
|
||||
*/
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE int_fast16_t
|
||||
ExponentComponent(T aValue)
|
||||
{
|
||||
/*
|
||||
* The exponent component of a float/double is an unsigned number, biased
|
||||
* from its actual value. Subtract the bias to retrieve the actual exponent.
|
||||
*/
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
Bits bits = BitwiseCast<Bits>(aValue);
|
||||
return int_fast16_t((bits & Traits::kExponentBits) >> Traits::kExponentShift) -
|
||||
int_fast16_t(Traits::kExponentBias);
|
||||
}
|
||||
|
||||
/** Returns +Infinity. */
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE T
|
||||
PositiveInfinity()
|
||||
{
|
||||
/*
|
||||
* Positive infinity has all exponent bits set, sign bit set to 0, and no
|
||||
* significand.
|
||||
*/
|
||||
typedef FloatingPoint<T> Traits;
|
||||
return BitwiseCast<T>(Traits::kExponentBits);
|
||||
}
|
||||
|
||||
/** Returns -Infinity. */
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE T
|
||||
NegativeInfinity()
|
||||
{
|
||||
/*
|
||||
* Negative infinity has all exponent bits set, sign bit set to 1, and no
|
||||
* significand.
|
||||
*/
|
||||
typedef FloatingPoint<T> Traits;
|
||||
return BitwiseCast<T>(Traits::kSignBit | Traits::kExponentBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bit pattern for a NaN with the specified sign bit and
|
||||
* significand bits.
|
||||
*/
|
||||
template<typename T,
|
||||
int SignBit,
|
||||
typename FloatingPoint<T>::Bits Significand>
|
||||
struct SpecificNaNBits
|
||||
{
|
||||
using Traits = FloatingPoint<T>;
|
||||
|
||||
static_assert(SignBit == 0 || SignBit == 1, "bad sign bit");
|
||||
static_assert((Significand & ~Traits::kSignificandBits) == 0,
|
||||
"significand must only have significand bits set");
|
||||
static_assert(Significand & Traits::kSignificandBits,
|
||||
"significand must be nonzero");
|
||||
|
||||
static constexpr typename Traits::Bits value =
|
||||
(SignBit * Traits::kSignBit) | Traits::kExponentBits | Significand;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a NaN value with the specified sign bit and significand bits.
|
||||
*
|
||||
* 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 T>
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
SpecificNaN(int signbit, typename FloatingPoint<T>::Bits significand, T* result)
|
||||
{
|
||||
typedef FloatingPoint<T> Traits;
|
||||
MOZ_ASSERT(signbit == 0 || signbit == 1);
|
||||
MOZ_ASSERT((significand & ~Traits::kSignificandBits) == 0);
|
||||
MOZ_ASSERT(significand & Traits::kSignificandBits);
|
||||
|
||||
BitwiseCast<T>((signbit ? Traits::kSignBit : 0) |
|
||||
Traits::kExponentBits |
|
||||
significand,
|
||||
result);
|
||||
MOZ_ASSERT(IsNaN(*result));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE T
|
||||
SpecificNaN(int signbit, typename FloatingPoint<T>::Bits significand)
|
||||
{
|
||||
T t;
|
||||
SpecificNaN(signbit, significand, &t);
|
||||
return t;
|
||||
}
|
||||
|
||||
/** Computes the smallest non-zero positive float/double value. */
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE T
|
||||
MinNumberValue()
|
||||
{
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
return BitwiseCast<T>(Bits(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* If aValue is equal to some int32_t value, set *aInt32 to that value and
|
||||
* return true; otherwise return false.
|
||||
*
|
||||
* Note that negative zero is "equal" to zero here. To test whether a value can
|
||||
* be losslessly converted to int32_t and back, use NumberIsInt32 instead.
|
||||
*/
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
NumberEqualsInt32(T aValue, int32_t* aInt32)
|
||||
{
|
||||
/*
|
||||
* XXX Casting a floating-point value that doesn't truncate to int32_t, to
|
||||
* int32_t, induces undefined behavior. We should definitely fix this
|
||||
* (bug 744965), but as apparently it "works" in practice, it's not a
|
||||
* pressing concern now.
|
||||
*/
|
||||
return aValue == (*aInt32 = int32_t(aValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* If d can be converted to int32_t and back to an identical double value,
|
||||
* set *aInt32 to that value and return true; otherwise return false.
|
||||
*
|
||||
* The difference between this and NumberEqualsInt32 is that this method returns
|
||||
* false for negative zero.
|
||||
*/
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
NumberIsInt32(T aValue, int32_t* aInt32)
|
||||
{
|
||||
return !IsNegativeZero(aValue) && NumberEqualsInt32(aValue, aInt32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a NaN value. Do not use this method if you depend upon a particular
|
||||
* NaN value being returned.
|
||||
*/
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE T
|
||||
UnspecifiedNaN()
|
||||
{
|
||||
/*
|
||||
* If we can use any quiet NaN, we might as well use the all-ones NaN,
|
||||
* since it's cheap to materialize on common platforms (such as x64, where
|
||||
* this value can be represented in a 32-bit signed immediate field, allowing
|
||||
* it to be stored to memory in a single instruction).
|
||||
*/
|
||||
typedef FloatingPoint<T> Traits;
|
||||
return SpecificNaN<T>(1, Traits::kSignificandBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two doubles for equality, *without* equating -0 to +0, and equating
|
||||
* any NaN value to any other NaN value. (The normal equality operators equate
|
||||
* -0 with +0, and they equate NaN to no other value.)
|
||||
*/
|
||||
template<typename T>
|
||||
static inline bool
|
||||
NumbersAreIdentical(T aValue1, T aValue2)
|
||||
{
|
||||
typedef FloatingPoint<T> Traits;
|
||||
typedef typename Traits::Bits Bits;
|
||||
if (IsNaN(aValue1)) {
|
||||
return IsNaN(aValue2);
|
||||
}
|
||||
return BitwiseCast<Bits>(aValue1) == BitwiseCast<Bits>(aValue2);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
struct FuzzyEqualsEpsilon;
|
||||
|
||||
template<>
|
||||
struct FuzzyEqualsEpsilon<float>
|
||||
{
|
||||
// A number near 1e-5 that is exactly representable in a float.
|
||||
static float value() { return 1.0f / (1 << 17); }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FuzzyEqualsEpsilon<double>
|
||||
{
|
||||
// A number near 1e-12 that is exactly representable in a double.
|
||||
static double value() { return 1.0 / (1LL << 40); }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Compare two floating point values for equality, modulo rounding error. That
|
||||
* is, the two values are considered equal if they are both not NaN and if they
|
||||
* are less than or equal to aEpsilon apart. The default value of aEpsilon is
|
||||
* near 1e-5.
|
||||
*
|
||||
* For most scenarios you will want to use FuzzyEqualsMultiplicative instead,
|
||||
* as it is more reasonable over the entire range of floating point numbers.
|
||||
* This additive version should only be used if you know the range of the
|
||||
* numbers you are dealing with is bounded and stays around the same order of
|
||||
* magnitude.
|
||||
*/
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
FuzzyEqualsAdditive(T aValue1, T aValue2,
|
||||
T aEpsilon = detail::FuzzyEqualsEpsilon<T>::value())
|
||||
{
|
||||
static_assert(IsFloatingPoint<T>::value, "floating point type required");
|
||||
return Abs(aValue1 - aValue2) <= aEpsilon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two floating point values for equality, allowing for rounding error
|
||||
* relative to the magnitude of the values. That is, the two values are
|
||||
* considered equal if they are both not NaN and they are less than or equal to
|
||||
* some aEpsilon apart, where the aEpsilon is scaled by the smaller of the two
|
||||
* argument values.
|
||||
*
|
||||
* In most cases you will want to use this rather than FuzzyEqualsAdditive, as
|
||||
* this function effectively masks out differences in the bottom few bits of
|
||||
* the floating point numbers being compared, regardless of what order of
|
||||
* magnitude those numbers are at.
|
||||
*/
|
||||
template<typename T>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
FuzzyEqualsMultiplicative(T aValue1, T aValue2,
|
||||
T aEpsilon = detail::FuzzyEqualsEpsilon<T>::value())
|
||||
{
|
||||
static_assert(IsFloatingPoint<T>::value, "floating point type required");
|
||||
// can't use std::min because of bug 965340
|
||||
T smaller = Abs(aValue1) < Abs(aValue2) ? Abs(aValue1) : Abs(aValue2);
|
||||
return Abs(aValue1 - aValue2) <= aEpsilon * smaller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given value can be losslessly represented as an IEEE-754
|
||||
* single format number, false otherwise. All NaN values are considered
|
||||
* representable (notwithstanding that the exact bit pattern of a double format
|
||||
* NaN value can't be exactly represented in single format).
|
||||
*
|
||||
* This function isn't inlined to avoid buggy optimizations by MSVC.
|
||||
*/
|
||||
MOZ_MUST_USE
|
||||
extern MFBT_API bool
|
||||
IsFloat32Representable(double aFloat32);
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_FloatingPoint_h */
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
/* -*- 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 type-erased callable wrapper. */
|
||||
|
||||
#ifndef mozilla_Function_h
|
||||
#define mozilla_Function_h
|
||||
|
||||
#include "mozilla/Attributes.h" // for MOZ_IMPLICIT
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
// |function<Signature>| is a wrapper that can hold any type of callable
|
||||
// object that can be invoked in a way that's compatible with |Signature|.
|
||||
// The standard "type erasure" technique is used to avoid the type of the
|
||||
// wrapper depending on the concrete type of the wrapped callable.
|
||||
//
|
||||
// Supported callable types include non-member functions, static member
|
||||
// functions, and function objects (that is to say, objects with an overloaded
|
||||
// call operator; this includes C++11 lambdas). Member functions aren't
|
||||
// directly supported; they first need to be wrapped into a function object
|
||||
// using |std::mem_fn()| or an equivalent.
|
||||
//
|
||||
// |Signature| is a type of the form |ReturnType(Arguments...)|. Syntactically,
|
||||
// this is a function type; it's not used in any way other than serving as a
|
||||
// vehicle to encode the return and argument types into a single type.
|
||||
//
|
||||
// |function| is default-constructible. A default-constructed instance is
|
||||
// considered "empty". Invoking an empty instance is undefined behaviour.
|
||||
// An empty instance can be populated with a callable by assigning to it.
|
||||
//
|
||||
// This class is intended to provide functionality similar to the C++11
|
||||
// standard library class |std::function|.
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename ReturnType, typename... Arguments>
|
||||
class FunctionImplBase : public mozilla::RefCounted<FunctionImplBase<ReturnType, Arguments...>>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(FunctionImplBase)
|
||||
|
||||
virtual ~FunctionImplBase() {}
|
||||
virtual ReturnType call(Arguments... aArguments) = 0;
|
||||
};
|
||||
|
||||
// Normal Callable Object.
|
||||
template <typename Callable, typename ReturnType, typename... Arguments>
|
||||
class FunctionImpl : public FunctionImplBase<ReturnType, Arguments...>
|
||||
{
|
||||
public:
|
||||
explicit FunctionImpl(const Callable& aCallable)
|
||||
: mCallable(aCallable) {}
|
||||
|
||||
ReturnType call(Arguments... aArguments) override
|
||||
{
|
||||
return mCallable(Forward<Arguments>(aArguments)...);
|
||||
}
|
||||
private:
|
||||
Callable mCallable;
|
||||
};
|
||||
|
||||
// Base class for passing pointer to member function.
|
||||
template <typename Callable, typename ReturnType, typename... Arguments>
|
||||
class MemberFunctionImplBase : public FunctionImplBase<ReturnType, Arguments...>
|
||||
{
|
||||
public:
|
||||
explicit MemberFunctionImplBase(const Callable& aCallable)
|
||||
: mCallable(aCallable) {}
|
||||
|
||||
ReturnType call(Arguments... aArguments) override
|
||||
{
|
||||
return callInternal(Forward<Arguments>(aArguments)...);
|
||||
}
|
||||
private:
|
||||
template<typename ThisType, typename... Args>
|
||||
ReturnType callInternal(ThisType* aThis, Args&&... aArguments)
|
||||
{
|
||||
return (aThis->*mCallable)(Forward<Args>(aArguments)...);
|
||||
}
|
||||
|
||||
template<typename ThisType, typename... Args>
|
||||
ReturnType callInternal(ThisType&& aThis, Args&&... aArguments)
|
||||
{
|
||||
return (aThis.*mCallable)(Forward<Args>(aArguments)...);
|
||||
}
|
||||
Callable mCallable;
|
||||
};
|
||||
|
||||
// For non-const member function specialization of FunctionImpl.
|
||||
template <typename ThisType, typename... Args, typename ReturnType, typename... Arguments>
|
||||
class FunctionImpl<ReturnType(ThisType::*)(Args...),
|
||||
ReturnType, Arguments...>
|
||||
: public MemberFunctionImplBase<ReturnType(ThisType::*)(Args...),
|
||||
ReturnType, Arguments...>
|
||||
{
|
||||
public:
|
||||
explicit FunctionImpl(ReturnType(ThisType::*aMemberFunc)(Args...))
|
||||
: MemberFunctionImplBase<ReturnType(ThisType::*)(Args...),
|
||||
ReturnType, Arguments...>(aMemberFunc)
|
||||
{}
|
||||
};
|
||||
|
||||
// For const member function specialization of FunctionImpl.
|
||||
template <typename ThisType, typename... Args, typename ReturnType, typename... Arguments>
|
||||
class FunctionImpl<ReturnType(ThisType::*)(Args...) const,
|
||||
ReturnType, Arguments...>
|
||||
: public MemberFunctionImplBase<ReturnType(ThisType::*)(Args...) const,
|
||||
ReturnType, Arguments...>
|
||||
{
|
||||
public:
|
||||
explicit FunctionImpl(ReturnType(ThisType::*aConstMemberFunc)(Args...) const)
|
||||
: MemberFunctionImplBase<ReturnType(ThisType::*)(Args...) const,
|
||||
ReturnType, Arguments...>(aConstMemberFunc)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// The primary template is never defined. As |Signature| is required to be
|
||||
// of the form |ReturnType(Arguments...)|, we only define a partial
|
||||
// specialization that matches this form. This allows us to use |ReturnType|
|
||||
// and |Arguments| in the definition of the specialization without having to
|
||||
// introspect |Signature|.
|
||||
template<typename Signature>
|
||||
class function;
|
||||
|
||||
template<typename ReturnType, typename... Arguments>
|
||||
class function<ReturnType(Arguments...)>
|
||||
{
|
||||
public:
|
||||
function() {}
|
||||
|
||||
// This constructor is implicit to match the interface of |std::function|.
|
||||
template <typename Callable>
|
||||
MOZ_IMPLICIT function(const Callable& aCallable)
|
||||
: mImpl(new detail::FunctionImpl<Callable, ReturnType, Arguments...>(aCallable))
|
||||
{}
|
||||
MOZ_IMPLICIT function(const function& aFunction)
|
||||
: mImpl(aFunction.mImpl)
|
||||
{}
|
||||
MOZ_IMPLICIT function(decltype(nullptr))
|
||||
{}
|
||||
|
||||
// Move constructor and move assingment operator.
|
||||
// These should be generated automatically, but MSVC doesn't do that yet.
|
||||
function(function&& aOther) : mImpl(Move(aOther.mImpl)) {}
|
||||
function& operator=(function&& aOther) {
|
||||
mImpl = Move(aOther.mImpl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
function& operator=(const Callable& aCallable)
|
||||
{
|
||||
mImpl = new detail::FunctionImpl<Callable, ReturnType, Arguments...>(aCallable);
|
||||
return *this;
|
||||
}
|
||||
function& operator=(const function& aFunction)
|
||||
{
|
||||
mImpl = aFunction.mImpl;
|
||||
return *this;
|
||||
}
|
||||
function& operator=(decltype(nullptr))
|
||||
{
|
||||
mImpl = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
ReturnType operator()(Args&&... aArguments) const
|
||||
{
|
||||
MOZ_ASSERT(mImpl);
|
||||
return mImpl->call(Forward<Args>(aArguments)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return bool(mImpl);
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: Consider implementing a small object optimization.
|
||||
RefPtr<detail::FunctionImplBase<ReturnType, Arguments...>> mImpl;
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
bool
|
||||
operator==(const function<Signature>& aX, decltype(nullptr))
|
||||
{
|
||||
return !aX;
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
bool
|
||||
operator==(decltype(nullptr), const function<Signature>& aX)
|
||||
{
|
||||
return !aX;
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
bool
|
||||
operator!=(const function<Signature>& aX, decltype(nullptr))
|
||||
{
|
||||
return bool(aX);
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
bool
|
||||
operator!=(decltype(nullptr), const function<Signature>& aX)
|
||||
{
|
||||
return bool(aX);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_Function_h */
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* Implementation of macros to ensure correct use of RAII Auto* objects. */
|
||||
|
||||
#ifndef mozilla_GuardObjects_h
|
||||
#define mozilla_GuardObjects_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
/**
|
||||
* A custom define is used rather than |mozPoisonValue()| due to cascading
|
||||
* build failures relating to how mfbt is linked on different operating
|
||||
* systems. See bug 1160253.
|
||||
*/
|
||||
#define MOZ_POISON uintptr_t(-1)
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* The following classes are designed to cause assertions to detect
|
||||
* inadvertent use of guard objects as temporaries. In other words,
|
||||
* when we have a guard object whose only purpose is its constructor and
|
||||
* destructor (and is never otherwise referenced), the intended use
|
||||
* might be:
|
||||
*
|
||||
* AutoRestore savePainting(mIsPainting);
|
||||
*
|
||||
* but is is easy to accidentally write:
|
||||
*
|
||||
* AutoRestore(mIsPainting);
|
||||
*
|
||||
* which compiles just fine, but runs the destructor well before the
|
||||
* intended time.
|
||||
*
|
||||
* They work by adding (#ifdef DEBUG) an additional parameter to the
|
||||
* guard object's constructor, with a default value, so that users of
|
||||
* the guard object's API do not need to do anything. The default value
|
||||
* of this parameter is a temporary object. C++ (ISO/IEC 14882:1998),
|
||||
* section 12.2 [class.temporary], clauses 4 and 5 seem to assume a
|
||||
* guarantee that temporaries are destroyed in the reverse of their
|
||||
* construction order, but I actually can't find a statement that that
|
||||
* is true in the general case (beyond the two specific cases mentioned
|
||||
* there). However, it seems to be true.
|
||||
*
|
||||
* These classes are intended to be used only via the macros immediately
|
||||
* below them:
|
||||
*
|
||||
* MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER declares (ifdef DEBUG) a member
|
||||
* variable, and should be put where a declaration of a private
|
||||
* member variable would be placed.
|
||||
* MOZ_GUARD_OBJECT_NOTIFIER_PARAM should be placed at the end of the
|
||||
* parameters to each constructor of the guard object; it declares
|
||||
* (ifdef DEBUG) an additional parameter. (But use the *_ONLY_PARAM
|
||||
* variant for constructors that take no other parameters.)
|
||||
* MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL should likewise be used in
|
||||
* the implementation of such constructors when they are not inline.
|
||||
* MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT should be used in
|
||||
* the implementation of such constructors to pass the parameter to
|
||||
* a base class that also uses these macros
|
||||
* MOZ_GUARD_OBJECT_NOTIFIER_INIT is a statement that belongs in each
|
||||
* constructor. It uses the parameter declared by
|
||||
* MOZ_GUARD_OBJECT_NOTIFIER_PARAM.
|
||||
*
|
||||
* For more details, and examples of using these macros, see
|
||||
* https://developer.mozilla.org/en/Using_RAII_classes_in_Mozilla
|
||||
*/
|
||||
class GuardObjectNotifier
|
||||
{
|
||||
private:
|
||||
bool* mStatementDone;
|
||||
|
||||
public:
|
||||
GuardObjectNotifier()
|
||||
: mStatementDone(reinterpret_cast<bool*>(MOZ_POISON))
|
||||
{
|
||||
}
|
||||
|
||||
~GuardObjectNotifier()
|
||||
{
|
||||
// Assert that the GuardObjectNotifier has been properly initialized by
|
||||
// using the |MOZ_GUARD_OBJECT_NOTIFIER_INIT| macro. A poison value is
|
||||
// used rather than a null check to appease static analyzers that were
|
||||
// (incorrectly) detecting null pointer dereferences.
|
||||
MOZ_ASSERT(mStatementDone != reinterpret_cast<bool*>(MOZ_POISON));
|
||||
*mStatementDone = true;
|
||||
}
|
||||
|
||||
void setStatementDone(bool* aStatementIsDone)
|
||||
{
|
||||
mStatementDone = aStatementIsDone;
|
||||
}
|
||||
};
|
||||
|
||||
class GuardObjectNotificationReceiver
|
||||
{
|
||||
private:
|
||||
bool mStatementDone;
|
||||
|
||||
public:
|
||||
GuardObjectNotificationReceiver() : mStatementDone(false) { }
|
||||
|
||||
~GuardObjectNotificationReceiver() {
|
||||
/*
|
||||
* Assert that the guard object was not used as a temporary. (Note that
|
||||
* this assert might also fire if init is not called because the guard
|
||||
* object's implementation is not using the above macros correctly.)
|
||||
*/
|
||||
MOZ_ASSERT(mStatementDone);
|
||||
}
|
||||
|
||||
void init(GuardObjectNotifier& aNotifier)
|
||||
{
|
||||
aNotifier.setStatementDone(&mStatementDone);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace mozilla */
|
||||
|
||||
#undef MOZ_POISON
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef DEBUG
|
||||
# define MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER \
|
||||
mozilla::detail::GuardObjectNotificationReceiver _mCheckNotUsedAsTemporary;
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_PARAM \
|
||||
, mozilla::detail::GuardObjectNotifier&& _notifier = \
|
||||
mozilla::detail::GuardObjectNotifier()
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM \
|
||||
mozilla::detail::GuardObjectNotifier&& _notifier = \
|
||||
mozilla::detail::GuardObjectNotifier()
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL \
|
||||
, mozilla::detail::GuardObjectNotifier&& _notifier
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL \
|
||||
mozilla::detail::GuardObjectNotifier&& _notifier
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT \
|
||||
, mozilla::Move(_notifier)
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT \
|
||||
mozilla::Move(_notifier)
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_INIT \
|
||||
do { _mCheckNotUsedAsTemporary.init(_notifier); } while (0)
|
||||
#else
|
||||
# define MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_PARAM
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT
|
||||
# define MOZ_GUARD_OBJECT_NOTIFIER_INIT do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* mozilla_GuardObjects_h */
|
||||
|
|
@ -1,389 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* Utilities for hashing. */
|
||||
|
||||
/*
|
||||
* This file exports functions for hashing data down to a 32-bit value,
|
||||
* including:
|
||||
*
|
||||
* - HashString Hash a char* or char16_t/wchar_t* of known or unknown
|
||||
* length.
|
||||
*
|
||||
* - HashBytes Hash a byte array of known length.
|
||||
*
|
||||
* - HashGeneric Hash one or more values. Currently, we support uint32_t,
|
||||
* types which can be implicitly cast to uint32_t, data
|
||||
* pointers, and function pointers.
|
||||
*
|
||||
* - AddToHash Add one or more values to the given hash. This supports the
|
||||
* same list of types as HashGeneric.
|
||||
*
|
||||
*
|
||||
* You can chain these functions together to hash complex objects. For example:
|
||||
*
|
||||
* class ComplexObject
|
||||
* {
|
||||
* char* mStr;
|
||||
* uint32_t mUint1, mUint2;
|
||||
* void (*mCallbackFn)();
|
||||
*
|
||||
* public:
|
||||
* uint32_t hash()
|
||||
* {
|
||||
* uint32_t hash = HashString(mStr);
|
||||
* hash = AddToHash(hash, mUint1, mUint2);
|
||||
* return AddToHash(hash, mCallbackFn);
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* If you want to hash an nsAString or nsACString, use the HashString functions
|
||||
* in nsHashKeys.h.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_HashFunctions_h
|
||||
#define mozilla_HashFunctions_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Char16.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* The golden ratio as a 32-bit fixed-point value.
|
||||
*/
|
||||
static const uint32_t kGoldenRatioU32 = 0x9E3779B9U;
|
||||
|
||||
inline uint32_t
|
||||
RotateBitsLeft32(uint32_t aValue, uint8_t aBits)
|
||||
{
|
||||
MOZ_ASSERT(aBits < 32);
|
||||
return (aValue << aBits) | (aValue >> (32 - aBits));
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline uint32_t
|
||||
AddU32ToHash(uint32_t aHash, uint32_t aValue)
|
||||
{
|
||||
/*
|
||||
* This is the meat of all our hash routines. This hash function is not
|
||||
* particularly sophisticated, but it seems to work well for our mostly
|
||||
* plain-text inputs. Implementation notes follow.
|
||||
*
|
||||
* Our use of the golden ratio here is arbitrary; we could pick almost any
|
||||
* number which:
|
||||
*
|
||||
* * is odd (because otherwise, all our hash values will be even)
|
||||
*
|
||||
* * has a reasonably-even mix of 1's and 0's (consider the extreme case
|
||||
* where we multiply by 0x3 or 0xeffffff -- this will not produce good
|
||||
* mixing across all bits of the hash).
|
||||
*
|
||||
* The rotation length of 5 is also arbitrary, although an odd number is again
|
||||
* preferable so our hash explores the whole universe of possible rotations.
|
||||
*
|
||||
* Finally, we multiply by the golden ratio *after* xor'ing, not before.
|
||||
* Otherwise, if |aHash| is 0 (as it often is for the beginning of a
|
||||
* message), the expression
|
||||
*
|
||||
* (kGoldenRatioU32 * RotateBitsLeft(aHash, 5)) |xor| aValue
|
||||
*
|
||||
* evaluates to |aValue|.
|
||||
*
|
||||
* (Number-theoretic aside: Because any odd number |m| is relatively prime to
|
||||
* our modulus (2^32), the list
|
||||
*
|
||||
* [x * m (mod 2^32) for 0 <= x < 2^32]
|
||||
*
|
||||
* has no duplicate elements. This means that multiplying by |m| does not
|
||||
* cause us to skip any possible hash values.
|
||||
*
|
||||
* It's also nice if |m| has large-ish order mod 2^32 -- that is, if the
|
||||
* smallest k such that m^k == 1 (mod 2^32) is large -- so we can safely
|
||||
* multiply our hash value by |m| a few times without negating the
|
||||
* multiplicative effect. Our golden ratio constant has order 2^29, which is
|
||||
* more than enough for our purposes.)
|
||||
*/
|
||||
return kGoldenRatioU32 * (RotateBitsLeft32(aHash, 5) ^ aValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* AddUintptrToHash takes sizeof(uintptr_t) as a template parameter.
|
||||
*/
|
||||
template<size_t PtrSize>
|
||||
inline uint32_t
|
||||
AddUintptrToHash(uint32_t aHash, uintptr_t aValue);
|
||||
|
||||
template<>
|
||||
inline uint32_t
|
||||
AddUintptrToHash<4>(uint32_t aHash, uintptr_t aValue)
|
||||
{
|
||||
return AddU32ToHash(aHash, static_cast<uint32_t>(aValue));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32_t
|
||||
AddUintptrToHash<8>(uint32_t aHash, uintptr_t aValue)
|
||||
{
|
||||
/*
|
||||
* The static cast to uint64_t below is necessary because this function
|
||||
* sometimes gets compiled on 32-bit platforms (yes, even though it's a
|
||||
* template and we never call this particular override in a 32-bit build). If
|
||||
* we do aValue >> 32 on a 32-bit machine, we're shifting a 32-bit uintptr_t
|
||||
* right 32 bits, and the compiler throws an error.
|
||||
*/
|
||||
uint32_t v1 = static_cast<uint32_t>(aValue);
|
||||
uint32_t v2 = static_cast<uint32_t>(static_cast<uint64_t>(aValue) >> 32);
|
||||
return AddU32ToHash(AddU32ToHash(aHash, v1), v2);
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
/**
|
||||
* AddToHash takes a hash and some values and returns a new hash based on the
|
||||
* inputs.
|
||||
*
|
||||
* Currently, we support hashing uint32_t's, values which we can implicitly
|
||||
* convert to uint32_t, data pointers, and function pointers.
|
||||
*/
|
||||
template<typename A>
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
AddToHash(uint32_t aHash, A aA)
|
||||
{
|
||||
/*
|
||||
* Try to convert |A| to uint32_t implicitly. If this works, great. If not,
|
||||
* we'll error out.
|
||||
*/
|
||||
return detail::AddU32ToHash(aHash, aA);
|
||||
}
|
||||
|
||||
template<typename A>
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
AddToHash(uint32_t aHash, A* aA)
|
||||
{
|
||||
/*
|
||||
* You might think this function should just take a void*. But then we'd only
|
||||
* catch data pointers and couldn't handle function pointers.
|
||||
*/
|
||||
|
||||
static_assert(sizeof(aA) == sizeof(uintptr_t), "Strange pointer!");
|
||||
|
||||
return detail::AddUintptrToHash<sizeof(uintptr_t)>(aHash, uintptr_t(aA));
|
||||
}
|
||||
|
||||
template<>
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
AddToHash(uint32_t aHash, uintptr_t aA)
|
||||
{
|
||||
return detail::AddUintptrToHash<sizeof(uintptr_t)>(aHash, aA);
|
||||
}
|
||||
|
||||
template<typename A, typename... Args>
|
||||
MOZ_MUST_USE uint32_t
|
||||
AddToHash(uint32_t aHash, A aArg, Args... aArgs)
|
||||
{
|
||||
return AddToHash(AddToHash(aHash, aArg), aArgs...);
|
||||
}
|
||||
|
||||
/**
|
||||
* The HashGeneric class of functions let you hash one or more values.
|
||||
*
|
||||
* If you want to hash together two values x and y, calling HashGeneric(x, y) is
|
||||
* much better than calling AddToHash(x, y), because AddToHash(x, y) assumes
|
||||
* that x has already been hashed.
|
||||
*/
|
||||
template<typename... Args>
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
HashGeneric(Args... aArgs)
|
||||
{
|
||||
return AddToHash(0, aArgs...);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
uint32_t
|
||||
HashUntilZero(const T* aStr)
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
for (T c; (c = *aStr); aStr++) {
|
||||
hash = AddToHash(hash, c);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint32_t
|
||||
HashKnownLength(const T* aStr, size_t aLength)
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
for (size_t i = 0; i < aLength; i++) {
|
||||
hash = AddToHash(hash, aStr[i]);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
/**
|
||||
* The HashString overloads below do just what you'd expect.
|
||||
*
|
||||
* If you have the string's length, you might as well call the overload which
|
||||
* includes the length. It may be marginally faster.
|
||||
*/
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
HashString(const char* aStr)
|
||||
{
|
||||
return detail::HashUntilZero(reinterpret_cast<const unsigned char*>(aStr));
|
||||
}
|
||||
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
HashString(const char* aStr, size_t aLength)
|
||||
{
|
||||
return detail::HashKnownLength(reinterpret_cast<const unsigned char*>(aStr), aLength);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE
|
||||
inline uint32_t
|
||||
HashString(const unsigned char* aStr, size_t aLength)
|
||||
{
|
||||
return detail::HashKnownLength(aStr, aLength);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
HashString(const char16_t* aStr)
|
||||
{
|
||||
return detail::HashUntilZero(aStr);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
HashString(const char16_t* aStr, size_t aLength)
|
||||
{
|
||||
return detail::HashKnownLength(aStr, aLength);
|
||||
}
|
||||
|
||||
/*
|
||||
* On Windows, wchar_t is not the same as char16_t, even though it's
|
||||
* the same width!
|
||||
*/
|
||||
#ifdef WIN32
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
HashString(const wchar_t* aStr)
|
||||
{
|
||||
return detail::HashUntilZero(aStr);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE inline uint32_t
|
||||
HashString(const wchar_t* aStr, size_t aLength)
|
||||
{
|
||||
return detail::HashKnownLength(aStr, aLength);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Hash some number of bytes.
|
||||
*
|
||||
* This hash walks word-by-word, rather than byte-by-byte, so you won't get the
|
||||
* same result out of HashBytes as you would out of HashString.
|
||||
*/
|
||||
MOZ_MUST_USE extern MFBT_API uint32_t
|
||||
HashBytes(const void* bytes, size_t aLength);
|
||||
|
||||
/**
|
||||
* A pseudorandom function mapping 32-bit integers to 32-bit integers.
|
||||
*
|
||||
* This is for when you're feeding private data (like pointer values or credit
|
||||
* card numbers) to a non-crypto hash function (like HashBytes) and then using
|
||||
* the hash code for something that untrusted parties could observe (like a JS
|
||||
* Map). Plug in a HashCodeScrambler before that last step to avoid leaking the
|
||||
* private data.
|
||||
*
|
||||
* By itself, this does not prevent hash-flooding DoS attacks, because an
|
||||
* attacker can still generate many values with exactly equal hash codes by
|
||||
* attacking the non-crypto hash function alone. Equal hash codes will, of
|
||||
* course, still be equal however much you scramble them.
|
||||
*
|
||||
* The algorithm is SipHash-1-3. See <https://131002.net/siphash/>.
|
||||
*/
|
||||
class HashCodeScrambler
|
||||
{
|
||||
struct SipHasher;
|
||||
|
||||
uint64_t mK0, mK1;
|
||||
|
||||
public:
|
||||
/** Creates a new scrambler with the given 128-bit key. */
|
||||
constexpr HashCodeScrambler(uint64_t aK0, uint64_t aK1) : mK0(aK0), mK1(aK1) {}
|
||||
|
||||
/**
|
||||
* Scramble a hash code. Always produces the same result for the same
|
||||
* combination of key and hash code.
|
||||
*/
|
||||
uint32_t scramble(uint32_t aHashCode) const
|
||||
{
|
||||
SipHasher hasher(mK0, mK1);
|
||||
return uint32_t(hasher.sipHash(aHashCode));
|
||||
}
|
||||
|
||||
private:
|
||||
struct SipHasher
|
||||
{
|
||||
SipHasher(uint64_t aK0, uint64_t aK1)
|
||||
{
|
||||
// 1. Initialization.
|
||||
mV0 = aK0 ^ UINT64_C(0x736f6d6570736575);
|
||||
mV1 = aK1 ^ UINT64_C(0x646f72616e646f6d);
|
||||
mV2 = aK0 ^ UINT64_C(0x6c7967656e657261);
|
||||
mV3 = aK1 ^ UINT64_C(0x7465646279746573);
|
||||
}
|
||||
|
||||
uint64_t sipHash(uint64_t aM)
|
||||
{
|
||||
// 2. Compression.
|
||||
mV3 ^= aM;
|
||||
sipRound();
|
||||
mV0 ^= aM;
|
||||
|
||||
// 3. Finalization.
|
||||
mV2 ^= 0xff;
|
||||
for (int i = 0; i < 3; i++)
|
||||
sipRound();
|
||||
return mV0 ^ mV1 ^ mV2 ^ mV3;
|
||||
}
|
||||
|
||||
void sipRound()
|
||||
{
|
||||
mV0 += mV1;
|
||||
mV1 = RotateLeft(mV1, 13);
|
||||
mV1 ^= mV0;
|
||||
mV0 = RotateLeft(mV0, 32);
|
||||
mV2 += mV3;
|
||||
mV3 = RotateLeft(mV3, 16);
|
||||
mV3 ^= mV2;
|
||||
mV0 += mV3;
|
||||
mV3 = RotateLeft(mV3, 21);
|
||||
mV3 ^= mV0;
|
||||
mV2 += mV1;
|
||||
mV1 = RotateLeft(mV1, 17);
|
||||
mV1 ^= mV2;
|
||||
mV2 = RotateLeft(mV2, 32);
|
||||
}
|
||||
|
||||
uint64_t mV0, mV1, mV2, mV3;
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* mozilla_HashFunctions_h */
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/* -*- 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 utility for expanding a tuple into a variadic argument list.
|
||||
* Based on std::index_sequence. */
|
||||
|
||||
/**
|
||||
* Example usage:
|
||||
*
|
||||
* Problem:
|
||||
*
|
||||
* You have a variadic function Foo:
|
||||
*
|
||||
* template <typename... Args> void Foo(Args...);
|
||||
*
|
||||
* And a variadic function Bar, which contains a tuple:
|
||||
*
|
||||
* template <typename... Args>
|
||||
* void Bar() {
|
||||
* // ...
|
||||
* Tuple<Args...> t;
|
||||
* }
|
||||
*
|
||||
* And inside Bar, you want to call Foo with the elements of the tuple as
|
||||
* arguments to Foo.
|
||||
*
|
||||
* You want to write:
|
||||
*
|
||||
* Foo(Get<0>(t), Get<1>(t), ..., Get<N>(t))
|
||||
*
|
||||
* but you can't literally write that, because N is different for different
|
||||
* instantiations of Bar.
|
||||
*
|
||||
* Solution:
|
||||
*
|
||||
* Write a helper function which takes the tuple, and an index sequence
|
||||
* containing indices corresponding to the tuple indices.
|
||||
*
|
||||
* template <typename... Args, size_t... Indices>
|
||||
* void Helper(const Tuple<Args...>& t, IndexSequence<Indices>)
|
||||
* {
|
||||
* Foo(Get<Indices>(t)...);
|
||||
* }
|
||||
*
|
||||
* Assuming 'Indices...' are 0, 1, ..., N - 1, where N is the size of the
|
||||
* tuple, pack expansion will expand the pack 'Get<Indices>(t)...' to
|
||||
* 'Get<0>(t), Get<1>(t), ..., Get<N>(t)'.
|
||||
*
|
||||
* Finally, call the helper, creating the index sequence to pass in like so:
|
||||
*
|
||||
* template <typename... Args>
|
||||
* void Bar() {
|
||||
* // ...
|
||||
* Tuple<Args...> t;
|
||||
* Helper(t, typename IndexSequenceFor<Args...>::Type());
|
||||
* }
|
||||
*/
|
||||
|
||||
#ifndef mozilla_IndexSequence_h
|
||||
#define mozilla_IndexSequence_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Represents a compile-time sequence of integer indices.
|
||||
*/
|
||||
template<size_t... Indices>
|
||||
struct IndexSequence
|
||||
{
|
||||
static constexpr size_t Size() { return sizeof...(Indices); }
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Helpers used by MakeIndexSequence.
|
||||
|
||||
template<size_t... Indices>
|
||||
struct IndexTuple
|
||||
{
|
||||
typedef IndexTuple<Indices..., sizeof...(Indices)> Next;
|
||||
};
|
||||
|
||||
// Builds IndexTuple<0, 1, ..., N - 1>.
|
||||
template<size_t N>
|
||||
struct BuildIndexTuple
|
||||
{
|
||||
typedef typename BuildIndexTuple<N - 1>::Type::Next Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct BuildIndexTuple<0>
|
||||
{
|
||||
typedef IndexTuple<> Type;
|
||||
};
|
||||
|
||||
template<size_t N, typename IndexTuple>
|
||||
struct MakeIndexSequenceImpl;
|
||||
|
||||
template<size_t N, size_t... Indices>
|
||||
struct MakeIndexSequenceImpl<N, IndexTuple<Indices...>>
|
||||
{
|
||||
typedef IndexSequence<Indices...> Type;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* A utility for building an IndexSequence of consecutive indices.
|
||||
* MakeIndexSequence<N>::Type evaluates to IndexSequence<0, 1, .., N - 1>.
|
||||
* Note: unlike std::make_index_sequence, this is not an alias template
|
||||
* to work around bugs in MSVC 2013.
|
||||
*/
|
||||
template<size_t N>
|
||||
struct MakeIndexSequence
|
||||
{
|
||||
typedef typename detail::MakeIndexSequenceImpl<N,
|
||||
typename detail::BuildIndexTuple<N>::Type>::Type Type;
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility for building an IndexSequence of consecutive indices
|
||||
* corresponding to a variadic argument list.
|
||||
* IndexSequenceFor<Types...> evaluates to IndexSequence<0, 1, ..., N - 1>
|
||||
* where N is the number of types in Types.
|
||||
* Note: unlike std::index_sequence_for, this is not an alias template
|
||||
* to work around bugs in MSVC 2013.
|
||||
*/
|
||||
template<typename... Types>
|
||||
struct IndexSequenceFor
|
||||
{
|
||||
typedef typename MakeIndexSequence<sizeof...(Types)>::Type Type;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_IndexSequence_h */
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/* -*- 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 the C99 <inttypes.h> interface. */
|
||||
|
||||
#ifndef mozilla_IntegerPrintfMacros_h_
|
||||
#define mozilla_IntegerPrintfMacros_h_
|
||||
|
||||
/*
|
||||
* These macros should not be used with the NSPR printf-like functions or their
|
||||
* users, e.g. mozilla/Logging.h. If you need to use NSPR's facilities, see the
|
||||
* comment on supported formats at the top of nsprpub/pr/include/prprf.h.
|
||||
*/
|
||||
|
||||
/*
|
||||
* scanf is a footgun: if the input number exceeds the bounds of the target
|
||||
* type, behavior is undefined (in the compiler sense: that is, this code
|
||||
* could overwrite your hard drive with zeroes):
|
||||
*
|
||||
* uint8_t u;
|
||||
* sscanf("256", "%" SCNu8, &u); // BAD
|
||||
*
|
||||
* For this reason, *never* use the SCN* macros provided by this header!
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/*
|
||||
* Fix up Android's broken [u]intptr_t inttype macros. Android's PRI*PTR
|
||||
* macros are defined as "ld", but sizeof(long) is 8 and sizeof(intptr_t)
|
||||
* is 4 on 32-bit Android. TestTypeTraits.cpp asserts that these new macro
|
||||
* definitions match the actual type sizes seen at compile time.
|
||||
*/
|
||||
#if defined(ANDROID) && !defined(__LP64__)
|
||||
# undef PRIdPTR /* intptr_t */
|
||||
# define PRIdPTR "d" /* intptr_t */
|
||||
# undef PRIiPTR /* intptr_t */
|
||||
# define PRIiPTR "i" /* intptr_t */
|
||||
# undef PRIoPTR /* uintptr_t */
|
||||
# define PRIoPTR "o" /* uintptr_t */
|
||||
# undef PRIuPTR /* uintptr_t */
|
||||
# define PRIuPTR "u" /* uintptr_t */
|
||||
# undef PRIxPTR /* uintptr_t */
|
||||
# define PRIxPTR "x" /* uintptr_t */
|
||||
# undef PRIXPTR /* uintptr_t */
|
||||
# define PRIXPTR "X" /* uintptr_t */
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_IntegerPrintfMacros_h_ */
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* Iterator over ranges of integers */
|
||||
|
||||
#ifndef mozilla_IntegerRange_h
|
||||
#define mozilla_IntegerRange_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ReverseIterator.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename IntTypeT>
|
||||
class IntegerIterator
|
||||
{
|
||||
public:
|
||||
template<typename IntType>
|
||||
explicit IntegerIterator(IntType aCurrent)
|
||||
: mCurrent(aCurrent) { }
|
||||
|
||||
template<typename IntType>
|
||||
explicit IntegerIterator(const IntegerIterator<IntType>& aOther)
|
||||
: mCurrent(aOther.mCurrent) { }
|
||||
|
||||
IntTypeT operator*() const { return mCurrent; }
|
||||
|
||||
/* Increment and decrement operators */
|
||||
|
||||
IntegerIterator& operator++() { ++mCurrent; return *this; }
|
||||
IntegerIterator& operator--() { --mCurrent; return *this; }
|
||||
IntegerIterator operator++(int) { auto ret = *this; ++mCurrent; return ret; }
|
||||
IntegerIterator operator--(int) { auto ret = *this; --mCurrent; return ret; }
|
||||
|
||||
/* Comparison operators */
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
friend bool operator==(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2);
|
||||
template<typename IntType1, typename IntType2>
|
||||
friend bool operator!=(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2);
|
||||
template<typename IntType1, typename IntType2>
|
||||
friend bool operator<(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2);
|
||||
template<typename IntType1, typename IntType2>
|
||||
friend bool operator<=(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2);
|
||||
template<typename IntType1, typename IntType2>
|
||||
friend bool operator>(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2);
|
||||
template<typename IntType1, typename IntType2>
|
||||
friend bool operator>=(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2);
|
||||
|
||||
private:
|
||||
IntTypeT mCurrent;
|
||||
};
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
bool operator==(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent == aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
bool operator!=(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent != aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
bool operator<(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent < aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
bool operator<=(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent <= aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
bool operator>(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent > aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
bool operator>=(const IntegerIterator<IntType1>& aIter1,
|
||||
const IntegerIterator<IntType2>& aIter2)
|
||||
{
|
||||
return aIter1.mCurrent >= aIter2.mCurrent;
|
||||
}
|
||||
|
||||
template<typename IntTypeT>
|
||||
class IntegerRange
|
||||
{
|
||||
public:
|
||||
typedef IntegerIterator<IntTypeT> iterator;
|
||||
typedef IntegerIterator<IntTypeT> const_iterator;
|
||||
typedef ReverseIterator<IntegerIterator<IntTypeT>> reverse_iterator;
|
||||
typedef ReverseIterator<IntegerIterator<IntTypeT>> const_reverse_iterator;
|
||||
|
||||
template<typename IntType>
|
||||
explicit IntegerRange(IntType aEnd)
|
||||
: mBegin(0), mEnd(aEnd) { }
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
IntegerRange(IntType1 aBegin, IntType2 aEnd)
|
||||
: mBegin(aBegin), mEnd(aEnd) { }
|
||||
|
||||
iterator begin() const { return iterator(mBegin); }
|
||||
const_iterator cbegin() const { return begin(); }
|
||||
iterator end() const { return iterator(mEnd); }
|
||||
const_iterator cend() const { return end(); }
|
||||
reverse_iterator rbegin() const { return reverse_iterator(mEnd); }
|
||||
const_reverse_iterator crbegin() const { return rbegin(); }
|
||||
reverse_iterator rend() const { return reverse_iterator(mBegin); }
|
||||
const_reverse_iterator crend() const { return rend(); }
|
||||
|
||||
private:
|
||||
IntTypeT mBegin;
|
||||
IntTypeT mEnd;
|
||||
};
|
||||
|
||||
template<typename T, bool = IsUnsigned<T>::value>
|
||||
struct GeqZero
|
||||
{
|
||||
static bool check(T t) {
|
||||
return t >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct GeqZero<T, true>
|
||||
{
|
||||
static bool check(T t) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename IntType>
|
||||
detail::IntegerRange<IntType>
|
||||
MakeRange(IntType aEnd)
|
||||
{
|
||||
static_assert(IsIntegral<IntType>::value, "value must be integral");
|
||||
MOZ_ASSERT(detail::GeqZero<IntType>::check(aEnd),
|
||||
"Should never have negative value here");
|
||||
return detail::IntegerRange<IntType>(aEnd);
|
||||
}
|
||||
|
||||
template<typename IntType1, typename IntType2>
|
||||
detail::IntegerRange<IntType2>
|
||||
MakeRange(IntType1 aBegin, IntType2 aEnd)
|
||||
{
|
||||
static_assert(IsIntegral<IntType1>::value && IsIntegral<IntType2>::value,
|
||||
"values must both be integral");
|
||||
static_assert(IsSigned<IntType1>::value == IsSigned<IntType2>::value,
|
||||
"signed/unsigned mismatch");
|
||||
MOZ_ASSERT(aEnd >= aBegin, "End value should be larger than begin value");
|
||||
return detail::IntegerRange<IntType2>(aBegin, aEnd);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_IntegerRange_h
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/* -*- 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
|
||||
|
|
@ -1,460 +0,0 @@
|
|||
/* -*- 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 JSON pretty-printer class. */
|
||||
|
||||
// A typical JSON-writing library requires you to first build up a data
|
||||
// structure that represents a JSON object and then serialize it (to file, or
|
||||
// somewhere else). This approach makes for a clean API, but building the data
|
||||
// structure takes up memory. Sometimes that isn't desirable, such as when the
|
||||
// JSON data is produced for memory reporting.
|
||||
//
|
||||
// The JSONWriter class instead allows JSON data to be written out
|
||||
// incrementally without building up large data structures.
|
||||
//
|
||||
// The API is slightly uglier than you would see in a typical JSON-writing
|
||||
// library, but still fairly easy to use. It's possible to generate invalid
|
||||
// JSON with JSONWriter, but typically the most basic testing will identify any
|
||||
// such problems.
|
||||
//
|
||||
// Similarly, there are no RAII facilities for automatically closing objects
|
||||
// and arrays. These would be nice if you are generating all your code within
|
||||
// nested functions, but in other cases you'd have to maintain an explicit
|
||||
// stack of RAII objects and manually unwind it, which is no better than just
|
||||
// calling "end" functions. Furthermore, the consequences of forgetting to
|
||||
// close an object or array are obvious and, again, will be identified via
|
||||
// basic testing, unlike other cases where RAII is typically used (e.g. smart
|
||||
// pointers) and the consequences of defects are more subtle.
|
||||
//
|
||||
// Importantly, the class does solve the two hard problems of JSON
|
||||
// pretty-printing, which are (a) correctly escaping strings, and (b) adding
|
||||
// appropriate indentation and commas between items.
|
||||
//
|
||||
// By default, every property is placed on its own line. However, it is
|
||||
// possible to request that objects and arrays be placed entirely on a single
|
||||
// line, which can reduce output size significantly in some cases.
|
||||
//
|
||||
// Strings used (for property names and string property values) are |const
|
||||
// char*| throughout, and can be ASCII or UTF-8.
|
||||
//
|
||||
// EXAMPLE
|
||||
// -------
|
||||
// Assume that |MyWriteFunc| is a class that implements |JSONWriteFunc|. The
|
||||
// following code:
|
||||
//
|
||||
// JSONWriter w(MakeUnique<MyWriteFunc>());
|
||||
// w.Start();
|
||||
// {
|
||||
// w.NullProperty("null");
|
||||
// w.BoolProperty("bool", true);
|
||||
// w.IntProperty("int", 1);
|
||||
// w.StartArrayProperty("array");
|
||||
// {
|
||||
// w.StringElement("string");
|
||||
// w.StartObjectElement();
|
||||
// {
|
||||
// w.DoubleProperty("double", 3.4);
|
||||
// w.StartArrayProperty("single-line array", w.SingleLineStyle);
|
||||
// {
|
||||
// w.IntElement(1);
|
||||
// w.StartObjectElement(); // SingleLineStyle is inherited from
|
||||
// w.EndObjectElement(); // above for this collection
|
||||
// }
|
||||
// w.EndArray();
|
||||
// }
|
||||
// w.EndObjectElement();
|
||||
// }
|
||||
// w.EndArrayProperty();
|
||||
// }
|
||||
// w.End();
|
||||
//
|
||||
// will produce pretty-printed output for the following JSON object:
|
||||
//
|
||||
// {
|
||||
// "null": null,
|
||||
// "bool": true,
|
||||
// "int": 1,
|
||||
// "array": [
|
||||
// "string",
|
||||
// {
|
||||
// "double": 3.4,
|
||||
// "single-line array": [1, {}]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// The nesting in the example code is obviously optional, but can aid
|
||||
// readability.
|
||||
|
||||
#ifndef mozilla_JSONWriter_h
|
||||
#define mozilla_JSONWriter_h
|
||||
|
||||
#include "mozilla/double-conversion.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// A quasi-functor for JSONWriter. We don't use a true functor because that
|
||||
// requires templatizing JSONWriter, and the templatization seeps to lots of
|
||||
// places we don't want it to.
|
||||
class JSONWriteFunc
|
||||
{
|
||||
public:
|
||||
virtual void Write(const char* aStr) = 0;
|
||||
virtual ~JSONWriteFunc() {}
|
||||
};
|
||||
|
||||
// Ideally this would be within |EscapedString| but when compiling with GCC
|
||||
// on Linux that caused link errors, whereas this formulation didn't.
|
||||
namespace detail {
|
||||
extern MFBT_DATA const char gTwoCharEscapes[256];
|
||||
} // namespace detail
|
||||
|
||||
class JSONWriter
|
||||
{
|
||||
// From http://www.ietf.org/rfc/rfc4627.txt:
|
||||
//
|
||||
// "All Unicode characters may be placed within the quotation marks except
|
||||
// for the characters that must be escaped: quotation mark, reverse
|
||||
// solidus, and the control characters (U+0000 through U+001F)."
|
||||
//
|
||||
// This implementation uses two-char escape sequences where possible, namely:
|
||||
//
|
||||
// \", \\, \b, \f, \n, \r, \t
|
||||
//
|
||||
// All control characters not in the above list are represented with a
|
||||
// six-char escape sequence, e.g. '\u000b' (a.k.a. '\v').
|
||||
//
|
||||
class EscapedString
|
||||
{
|
||||
// Only one of |mUnownedStr| and |mOwnedStr| are ever non-null. |mIsOwned|
|
||||
// indicates which one is in use. They're not within a union because that
|
||||
// wouldn't work with UniquePtr.
|
||||
bool mIsOwned;
|
||||
const char* mUnownedStr;
|
||||
UniquePtr<char[]> mOwnedStr;
|
||||
|
||||
void SanityCheck() const
|
||||
{
|
||||
MOZ_ASSERT_IF( mIsOwned, mOwnedStr.get() && !mUnownedStr);
|
||||
MOZ_ASSERT_IF(!mIsOwned, !mOwnedStr.get() && mUnownedStr);
|
||||
}
|
||||
|
||||
static char hexDigitToAsciiChar(uint8_t u)
|
||||
{
|
||||
u = u & 0xf;
|
||||
return u < 10 ? '0' + u : 'a' + (u - 10);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit EscapedString(const char* aStr)
|
||||
: mUnownedStr(nullptr)
|
||||
, mOwnedStr(nullptr)
|
||||
{
|
||||
const char* p;
|
||||
|
||||
// First, see if we need to modify the string.
|
||||
size_t nExtra = 0;
|
||||
p = aStr;
|
||||
while (true) {
|
||||
uint8_t u = *p; // ensure it can't be interpreted as negative
|
||||
if (u == 0) {
|
||||
break;
|
||||
}
|
||||
if (detail::gTwoCharEscapes[u]) {
|
||||
nExtra += 1;
|
||||
} else if (u <= 0x1f) {
|
||||
nExtra += 5;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (nExtra == 0) {
|
||||
// No escapes needed. Easy.
|
||||
mIsOwned = false;
|
||||
mUnownedStr = aStr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Escapes are needed. We'll create a new string.
|
||||
mIsOwned = true;
|
||||
size_t len = (p - aStr) + nExtra;
|
||||
mOwnedStr = MakeUnique<char[]>(len + 1);
|
||||
|
||||
p = aStr;
|
||||
size_t i = 0;
|
||||
|
||||
while (true) {
|
||||
uint8_t u = *p; // ensure it can't be interpreted as negative
|
||||
if (u == 0) {
|
||||
mOwnedStr[i] = 0;
|
||||
break;
|
||||
}
|
||||
if (detail::gTwoCharEscapes[u]) {
|
||||
mOwnedStr[i++] = '\\';
|
||||
mOwnedStr[i++] = detail::gTwoCharEscapes[u];
|
||||
} else if (u <= 0x1f) {
|
||||
mOwnedStr[i++] = '\\';
|
||||
mOwnedStr[i++] = 'u';
|
||||
mOwnedStr[i++] = '0';
|
||||
mOwnedStr[i++] = '0';
|
||||
mOwnedStr[i++] = hexDigitToAsciiChar((u & 0x00f0) >> 4);
|
||||
mOwnedStr[i++] = hexDigitToAsciiChar(u & 0x000f);
|
||||
} else {
|
||||
mOwnedStr[i++] = u;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
~EscapedString()
|
||||
{
|
||||
SanityCheck();
|
||||
}
|
||||
|
||||
const char* get() const
|
||||
{
|
||||
SanityCheck();
|
||||
return mIsOwned ? mOwnedStr.get() : mUnownedStr;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// Collections (objects and arrays) are printed in a multi-line style by
|
||||
// default. This can be changed to a single-line style if SingleLineStyle is
|
||||
// specified. If a collection is printed in single-line style, every nested
|
||||
// collection within it is also printed in single-line style, even if
|
||||
// multi-line style is requested.
|
||||
enum CollectionStyle {
|
||||
MultiLineStyle, // the default
|
||||
SingleLineStyle
|
||||
};
|
||||
|
||||
protected:
|
||||
const UniquePtr<JSONWriteFunc> mWriter;
|
||||
Vector<bool, 8> mNeedComma; // do we need a comma at depth N?
|
||||
Vector<bool, 8> mNeedNewlines; // do we need newlines at depth N?
|
||||
size_t mDepth; // the current nesting depth
|
||||
|
||||
void Indent()
|
||||
{
|
||||
for (size_t i = 0; i < mDepth; i++) {
|
||||
mWriter->Write(" ");
|
||||
}
|
||||
}
|
||||
|
||||
// Adds whatever is necessary (maybe a comma, and then a newline and
|
||||
// whitespace) to separate an item (property or element) from what's come
|
||||
// before.
|
||||
void Separator()
|
||||
{
|
||||
if (mNeedComma[mDepth]) {
|
||||
mWriter->Write(",");
|
||||
}
|
||||
if (mDepth > 0 && mNeedNewlines[mDepth]) {
|
||||
mWriter->Write("\n");
|
||||
Indent();
|
||||
} else if (mNeedComma[mDepth]) {
|
||||
mWriter->Write(" ");
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyNameAndColon(const char* aName)
|
||||
{
|
||||
EscapedString escapedName(aName);
|
||||
mWriter->Write("\"");
|
||||
mWriter->Write(escapedName.get());
|
||||
mWriter->Write("\": ");
|
||||
}
|
||||
|
||||
void Scalar(const char* aMaybePropertyName, const char* aStringValue)
|
||||
{
|
||||
Separator();
|
||||
if (aMaybePropertyName) {
|
||||
PropertyNameAndColon(aMaybePropertyName);
|
||||
}
|
||||
mWriter->Write(aStringValue);
|
||||
mNeedComma[mDepth] = true;
|
||||
}
|
||||
|
||||
void QuotedScalar(const char* aMaybePropertyName, const char* aStringValue)
|
||||
{
|
||||
Separator();
|
||||
if (aMaybePropertyName) {
|
||||
PropertyNameAndColon(aMaybePropertyName);
|
||||
}
|
||||
mWriter->Write("\"");
|
||||
mWriter->Write(aStringValue);
|
||||
mWriter->Write("\"");
|
||||
mNeedComma[mDepth] = true;
|
||||
}
|
||||
|
||||
void NewVectorEntries()
|
||||
{
|
||||
// If these tiny allocations OOM we might as well just crash because we
|
||||
// must be in serious memory trouble.
|
||||
MOZ_RELEASE_ASSERT(mNeedComma.resizeUninitialized(mDepth + 1));
|
||||
MOZ_RELEASE_ASSERT(mNeedNewlines.resizeUninitialized(mDepth + 1));
|
||||
mNeedComma[mDepth] = false;
|
||||
mNeedNewlines[mDepth] = true;
|
||||
}
|
||||
|
||||
void StartCollection(const char* aMaybePropertyName, const char* aStartChar,
|
||||
CollectionStyle aStyle = MultiLineStyle)
|
||||
{
|
||||
Separator();
|
||||
if (aMaybePropertyName) {
|
||||
mWriter->Write("\"");
|
||||
mWriter->Write(aMaybePropertyName);
|
||||
mWriter->Write("\": ");
|
||||
}
|
||||
mWriter->Write(aStartChar);
|
||||
mNeedComma[mDepth] = true;
|
||||
mDepth++;
|
||||
NewVectorEntries();
|
||||
mNeedNewlines[mDepth] =
|
||||
mNeedNewlines[mDepth - 1] && aStyle == MultiLineStyle;
|
||||
}
|
||||
|
||||
// Adds the whitespace and closing char necessary to end a collection.
|
||||
void EndCollection(const char* aEndChar)
|
||||
{
|
||||
if (mNeedNewlines[mDepth]) {
|
||||
mWriter->Write("\n");
|
||||
mDepth--;
|
||||
Indent();
|
||||
} else {
|
||||
mDepth--;
|
||||
}
|
||||
mWriter->Write(aEndChar);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit JSONWriter(UniquePtr<JSONWriteFunc> aWriter)
|
||||
: mWriter(Move(aWriter))
|
||||
, mNeedComma()
|
||||
, mNeedNewlines()
|
||||
, mDepth(0)
|
||||
{
|
||||
NewVectorEntries();
|
||||
}
|
||||
|
||||
// Returns the JSONWriteFunc passed in at creation, for temporary use. The
|
||||
// JSONWriter object still owns the JSONWriteFunc.
|
||||
JSONWriteFunc* WriteFunc() const { return mWriter.get(); }
|
||||
|
||||
// For all the following functions, the "Prints:" comment indicates what the
|
||||
// basic output looks like. However, it doesn't indicate the whitespace and
|
||||
// trailing commas, which are automatically added as required.
|
||||
//
|
||||
// All property names and string properties are escaped as necessary.
|
||||
|
||||
// Prints: {
|
||||
void Start(CollectionStyle aStyle = MultiLineStyle)
|
||||
{
|
||||
StartCollection(nullptr, "{", aStyle);
|
||||
}
|
||||
|
||||
// Prints: }
|
||||
void End() { EndCollection("}\n"); }
|
||||
|
||||
// Prints: "<aName>": null
|
||||
void NullProperty(const char* aName)
|
||||
{
|
||||
Scalar(aName, "null");
|
||||
}
|
||||
|
||||
// Prints: null
|
||||
void NullElement() { NullProperty(nullptr); }
|
||||
|
||||
// Prints: "<aName>": <aBool>
|
||||
void BoolProperty(const char* aName, bool aBool)
|
||||
{
|
||||
Scalar(aName, aBool ? "true" : "false");
|
||||
}
|
||||
|
||||
// Prints: <aBool>
|
||||
void BoolElement(bool aBool) { BoolProperty(nullptr, aBool); }
|
||||
|
||||
// Prints: "<aName>": <aInt>
|
||||
void IntProperty(const char* aName, int64_t aInt)
|
||||
{
|
||||
char buf[64];
|
||||
SprintfLiteral(buf, "%" PRId64, aInt);
|
||||
Scalar(aName, buf);
|
||||
}
|
||||
|
||||
// Prints: <aInt>
|
||||
void IntElement(int64_t aInt) { IntProperty(nullptr, aInt); }
|
||||
|
||||
// Prints: "<aName>": <aDouble>
|
||||
void DoubleProperty(const char* aName, double aDouble)
|
||||
{
|
||||
static const size_t buflen = 64;
|
||||
char buf[buflen];
|
||||
const double_conversion::DoubleToStringConverter &converter =
|
||||
double_conversion::DoubleToStringConverter::EcmaScriptConverter();
|
||||
double_conversion::StringBuilder builder(buf, buflen);
|
||||
converter.ToShortest(aDouble, &builder);
|
||||
Scalar(aName, builder.Finalize());
|
||||
}
|
||||
|
||||
// Prints: <aDouble>
|
||||
void DoubleElement(double aDouble) { DoubleProperty(nullptr, aDouble); }
|
||||
|
||||
// Prints: "<aName>": "<aStr>"
|
||||
void StringProperty(const char* aName, const char* aStr)
|
||||
{
|
||||
EscapedString escapedStr(aStr);
|
||||
QuotedScalar(aName, escapedStr.get());
|
||||
}
|
||||
|
||||
// Prints: "<aStr>"
|
||||
void StringElement(const char* aStr) { StringProperty(nullptr, aStr); }
|
||||
|
||||
// Prints: "<aName>": [
|
||||
void StartArrayProperty(const char* aName,
|
||||
CollectionStyle aStyle = MultiLineStyle)
|
||||
{
|
||||
StartCollection(aName, "[", aStyle);
|
||||
}
|
||||
|
||||
// Prints: [
|
||||
void StartArrayElement(CollectionStyle aStyle = MultiLineStyle)
|
||||
{
|
||||
StartArrayProperty(nullptr, aStyle);
|
||||
}
|
||||
|
||||
// Prints: ]
|
||||
void EndArray() { EndCollection("]"); }
|
||||
|
||||
// Prints: "<aName>": {
|
||||
void StartObjectProperty(const char* aName,
|
||||
CollectionStyle aStyle = MultiLineStyle)
|
||||
{
|
||||
StartCollection(aName, "{", aStyle);
|
||||
}
|
||||
|
||||
// Prints: {
|
||||
void StartObjectElement(CollectionStyle aStyle = MultiLineStyle)
|
||||
{
|
||||
StartObjectProperty(nullptr, aStyle);
|
||||
}
|
||||
|
||||
// Prints: }
|
||||
void EndObject() { EndCollection("}"); }
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_JSONWriter_h */
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
/* -*- 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 */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue