mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2026-01-11 00:27:02 +00:00
初始化
This commit is contained in:
@@ -0,0 +1,530 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
//#include "mozilla/Assertions.h"
|
||||
//#include "mozilla/EndianUtils.h"
|
||||
#include "SHA1.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <stdlib.h>
|
||||
# pragma intrinsic(_byteswap_ushort)
|
||||
# pragma intrinsic(_byteswap_ulong)
|
||||
# pragma intrinsic(_byteswap_uint64)
|
||||
#endif
|
||||
|
||||
#define MOZ_ASSERT(cond, ...) assert(cond)
|
||||
|
||||
//using se::NativeEndian;
|
||||
using se::SHA1Sum;
|
||||
|
||||
namespace {
|
||||
|
||||
#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
|
||||
|
||||
enum Endianness { Little, Big };
|
||||
|
||||
#if MOZ_BIG_ENDIAN
|
||||
# define MOZ_NATIVE_ENDIANNESS Big
|
||||
#else
|
||||
# define MOZ_NATIVE_ENDIANNESS Little
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
};
|
||||
|
||||
template<Endianness ThisEndian>
|
||||
class Endian
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
static T swapToBigEndian(T aValue)
|
||||
{
|
||||
return maybeSwap<ThisEndian, Big>(aValue);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
|
||||
class NativeEndian final : public Endian<MOZ_NATIVE_ENDIANNESS>
|
||||
{
|
||||
private:
|
||||
typedef Endian<MOZ_NATIVE_ENDIANNESS> super;
|
||||
|
||||
public:
|
||||
using super::swapToBigEndian;
|
||||
};
|
||||
|
||||
} // namespace {
|
||||
|
||||
static inline uint32_t
|
||||
SHA_ROTL(uint32_t aT, uint32_t aN)
|
||||
{
|
||||
MOZ_ASSERT(aN < 32);
|
||||
return (aT << aN) | (aT >> (32 - aN));
|
||||
}
|
||||
|
||||
static void
|
||||
shaCompress(volatile unsigned* aX, const uint32_t* aBuf);
|
||||
|
||||
#define SHA_F1(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z))
|
||||
#define SHA_F2(X, Y, Z) ((X) ^ (Y) ^ (Z))
|
||||
#define SHA_F3(X, Y, Z) (((X) & (Y)) | ((Z) & ((X) | (Y))))
|
||||
#define SHA_F4(X, Y, Z) ((X) ^ (Y) ^ (Z))
|
||||
|
||||
#define SHA_MIX(n, a, b, c) XW(n) = SHA_ROTL(XW(a) ^ XW(b) ^ XW(c) ^XW(n), 1)
|
||||
|
||||
SHA1Sum::SHA1Sum()
|
||||
: mSize(0), mDone(false)
|
||||
{
|
||||
// Initialize H with constants from FIPS180-1.
|
||||
mH[0] = 0x67452301L;
|
||||
mH[1] = 0xefcdab89L;
|
||||
mH[2] = 0x98badcfeL;
|
||||
mH[3] = 0x10325476L;
|
||||
mH[4] = 0xc3d2e1f0L;
|
||||
}
|
||||
|
||||
/*
|
||||
* Explanation of H array and index values:
|
||||
*
|
||||
* The context's H array is actually the concatenation of two arrays
|
||||
* defined by SHA1, the H array of state variables (5 elements),
|
||||
* and the W array of intermediate values, of which there are 16 elements.
|
||||
* The W array starts at H[5], that is W[0] is H[5].
|
||||
* Although these values are defined as 32-bit values, we use 64-bit
|
||||
* variables to hold them because the AMD64 stores 64 bit values in
|
||||
* memory MUCH faster than it stores any smaller values.
|
||||
*
|
||||
* Rather than passing the context structure to shaCompress, we pass
|
||||
* this combined array of H and W values. We do not pass the address
|
||||
* of the first element of this array, but rather pass the address of an
|
||||
* element in the middle of the array, element X. Presently X[0] is H[11].
|
||||
* So we pass the address of H[11] as the address of array X to shaCompress.
|
||||
* Then shaCompress accesses the members of the array using positive AND
|
||||
* negative indexes.
|
||||
*
|
||||
* Pictorially: (each element is 8 bytes)
|
||||
* H | H0 H1 H2 H3 H4 W0 W1 W2 W3 W4 W5 W6 W7 W8 W9 Wa Wb Wc Wd We Wf |
|
||||
* X |-11-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 |
|
||||
*
|
||||
* The byte offset from X[0] to any member of H and W is always
|
||||
* representable in a signed 8-bit value, which will be encoded
|
||||
* as a single byte offset in the X86-64 instruction set.
|
||||
* If we didn't pass the address of H[11], and instead passed the
|
||||
* address of H[0], the offsets to elements H[16] and above would be
|
||||
* greater than 127, not representable in a signed 8-bit value, and the
|
||||
* x86-64 instruction set would encode every such offset as a 32-bit
|
||||
* signed number in each instruction that accessed element H[16] or
|
||||
* higher. This results in much bigger and slower code.
|
||||
*/
|
||||
#define H2X 11 /* X[0] is H[11], and H[0] is X[-11] */
|
||||
#define W2X 6 /* X[0] is W[6], and W[0] is X[-6] */
|
||||
|
||||
/*
|
||||
* SHA: Add data to context.
|
||||
*/
|
||||
void
|
||||
SHA1Sum::update(const void* aData, uint32_t aLen)
|
||||
{
|
||||
MOZ_ASSERT(!mDone, "SHA1Sum can only be used to compute a single hash.");
|
||||
|
||||
const uint8_t* data = static_cast<const uint8_t*>(aData);
|
||||
|
||||
if (aLen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Accumulate the byte count. */
|
||||
unsigned int lenB = static_cast<unsigned int>(mSize) & 63U;
|
||||
|
||||
mSize += aLen;
|
||||
|
||||
/* Read the data into W and process blocks as they get full. */
|
||||
unsigned int togo;
|
||||
if (lenB > 0) {
|
||||
togo = 64U - lenB;
|
||||
if (aLen < togo) {
|
||||
togo = aLen;
|
||||
}
|
||||
memcpy(mU.mB + lenB, data, togo);
|
||||
aLen -= togo;
|
||||
data += togo;
|
||||
lenB = (lenB + togo) & 63U;
|
||||
if (!lenB) {
|
||||
shaCompress(&mH[H2X], mU.mW);
|
||||
}
|
||||
}
|
||||
|
||||
while (aLen >= 64U) {
|
||||
aLen -= 64U;
|
||||
shaCompress(&mH[H2X], reinterpret_cast<const uint32_t*>(data));
|
||||
data += 64U;
|
||||
}
|
||||
|
||||
if (aLen > 0) {
|
||||
memcpy(mU.mB, data, aLen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SHA: Generate hash value
|
||||
*/
|
||||
void
|
||||
SHA1Sum::finish(SHA1Sum::Hash& aHashOut)
|
||||
{
|
||||
MOZ_ASSERT(!mDone, "SHA1Sum can only be used to compute a single hash.");
|
||||
|
||||
uint64_t size = mSize;
|
||||
uint32_t lenB = uint32_t(size) & 63;
|
||||
|
||||
static const uint8_t bulk_pad[64] =
|
||||
{ 0x80,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
|
||||
|
||||
/* Pad with a binary 1 (e.g. 0x80), then zeroes, then length in bits. */
|
||||
update(bulk_pad, (((55 + 64) - lenB) & 63) + 1);
|
||||
MOZ_ASSERT((uint32_t(mSize) & 63) == 56);
|
||||
|
||||
/* Convert size from bytes to bits. */
|
||||
size <<= 3;
|
||||
mU.mW[14] = NativeEndian::swapToBigEndian(uint32_t(size >> 32));
|
||||
mU.mW[15] = NativeEndian::swapToBigEndian(uint32_t(size));
|
||||
shaCompress(&mH[H2X], mU.mW);
|
||||
|
||||
/* Output hash. */
|
||||
mU.mW[0] = NativeEndian::swapToBigEndian(mH[0]);
|
||||
mU.mW[1] = NativeEndian::swapToBigEndian(mH[1]);
|
||||
mU.mW[2] = NativeEndian::swapToBigEndian(mH[2]);
|
||||
mU.mW[3] = NativeEndian::swapToBigEndian(mH[3]);
|
||||
mU.mW[4] = NativeEndian::swapToBigEndian(mH[4]);
|
||||
memcpy(aHashOut, mU.mW, 20);
|
||||
mDone = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA: Compression function, unrolled.
|
||||
*
|
||||
* Some operations in shaCompress are done as 5 groups of 16 operations.
|
||||
* Others are done as 4 groups of 20 operations.
|
||||
* The code below shows that structure.
|
||||
*
|
||||
* The functions that compute the new values of the 5 state variables
|
||||
* A-E are done in 4 groups of 20 operations (or you may also think
|
||||
* of them as being done in 16 groups of 5 operations). They are
|
||||
* done by the SHA_RNDx macros below, in the right column.
|
||||
*
|
||||
* The functions that set the 16 values of the W array are done in
|
||||
* 5 groups of 16 operations. The first group is done by the
|
||||
* LOAD macros below, the latter 4 groups are done by SHA_MIX below,
|
||||
* in the left column.
|
||||
*
|
||||
* gcc's optimizer observes that each member of the W array is assigned
|
||||
* a value 5 times in this code. It reduces the number of store
|
||||
* operations done to the W array in the context (that is, in the X array)
|
||||
* by creating a W array on the stack, and storing the W values there for
|
||||
* the first 4 groups of operations on W, and storing the values in the
|
||||
* context's W array only in the fifth group. This is undesirable.
|
||||
* It is MUCH bigger code than simply using the context's W array, because
|
||||
* all the offsets to the W array in the stack are 32-bit signed offsets,
|
||||
* and it is no faster than storing the values in the context's W array.
|
||||
*
|
||||
* The original code for sha_fast.c prevented this creation of a separate
|
||||
* W array in the stack by creating a W array of 80 members, each of
|
||||
* whose elements is assigned only once. It also separated the computations
|
||||
* of the W array values and the computations of the values for the 5
|
||||
* state variables into two separate passes, W's, then A-E's so that the
|
||||
* second pass could be done all in registers (except for accessing the W
|
||||
* array) on machines with fewer registers. The method is suboptimal
|
||||
* for machines with enough registers to do it all in one pass, and it
|
||||
* necessitates using many instructions with 32-bit offsets.
|
||||
*
|
||||
* This code eliminates the separate W array on the stack by a completely
|
||||
* different means: by declaring the X array volatile. This prevents
|
||||
* the optimizer from trying to reduce the use of the X array by the
|
||||
* creation of a MORE expensive W array on the stack. The result is
|
||||
* that all instructions use signed 8-bit offsets and not 32-bit offsets.
|
||||
*
|
||||
* The combination of this code and the -O3 optimizer flag on GCC 3.4.3
|
||||
* results in code that is 3 times faster than the previous NSS sha_fast
|
||||
* code on AMD64.
|
||||
*/
|
||||
static void
|
||||
shaCompress(volatile unsigned* aX, const uint32_t* aBuf)
|
||||
{
|
||||
unsigned A, B, C, D, E;
|
||||
|
||||
#define XH(n) aX[n - H2X]
|
||||
#define XW(n) aX[n - W2X]
|
||||
|
||||
#define K0 0x5a827999L
|
||||
#define K1 0x6ed9eba1L
|
||||
#define K2 0x8f1bbcdcL
|
||||
#define K3 0xca62c1d6L
|
||||
|
||||
#define SHA_RND1(a, b, c, d, e, n) \
|
||||
a = SHA_ROTL(b, 5) + SHA_F1(c, d, e) + a + XW(n) + K0; c = SHA_ROTL(c, 30)
|
||||
#define SHA_RND2(a, b, c, d, e, n) \
|
||||
a = SHA_ROTL(b, 5) + SHA_F2(c, d, e) + a + XW(n) + K1; c = SHA_ROTL(c, 30)
|
||||
#define SHA_RND3(a, b, c, d, e, n) \
|
||||
a = SHA_ROTL(b, 5) + SHA_F3(c, d, e) + a + XW(n) + K2; c = SHA_ROTL(c, 30)
|
||||
#define SHA_RND4(a, b, c, d, e, n) \
|
||||
a = SHA_ROTL(b ,5) + SHA_F4(c, d, e) + a + XW(n) + K3; c = SHA_ROTL(c, 30)
|
||||
|
||||
#define LOAD(n) XW(n) = NativeEndian::swapToBigEndian(aBuf[n])
|
||||
|
||||
A = XH(0);
|
||||
B = XH(1);
|
||||
C = XH(2);
|
||||
D = XH(3);
|
||||
E = XH(4);
|
||||
|
||||
LOAD(0); SHA_RND1(E,A,B,C,D, 0);
|
||||
LOAD(1); SHA_RND1(D,E,A,B,C, 1);
|
||||
LOAD(2); SHA_RND1(C,D,E,A,B, 2);
|
||||
LOAD(3); SHA_RND1(B,C,D,E,A, 3);
|
||||
LOAD(4); SHA_RND1(A,B,C,D,E, 4);
|
||||
LOAD(5); SHA_RND1(E,A,B,C,D, 5);
|
||||
LOAD(6); SHA_RND1(D,E,A,B,C, 6);
|
||||
LOAD(7); SHA_RND1(C,D,E,A,B, 7);
|
||||
LOAD(8); SHA_RND1(B,C,D,E,A, 8);
|
||||
LOAD(9); SHA_RND1(A,B,C,D,E, 9);
|
||||
LOAD(10); SHA_RND1(E,A,B,C,D,10);
|
||||
LOAD(11); SHA_RND1(D,E,A,B,C,11);
|
||||
LOAD(12); SHA_RND1(C,D,E,A,B,12);
|
||||
LOAD(13); SHA_RND1(B,C,D,E,A,13);
|
||||
LOAD(14); SHA_RND1(A,B,C,D,E,14);
|
||||
LOAD(15); SHA_RND1(E,A,B,C,D,15);
|
||||
|
||||
SHA_MIX( 0, 13, 8, 2); SHA_RND1(D,E,A,B,C, 0);
|
||||
SHA_MIX( 1, 14, 9, 3); SHA_RND1(C,D,E,A,B, 1);
|
||||
SHA_MIX( 2, 15, 10, 4); SHA_RND1(B,C,D,E,A, 2);
|
||||
SHA_MIX( 3, 0, 11, 5); SHA_RND1(A,B,C,D,E, 3);
|
||||
|
||||
SHA_MIX( 4, 1, 12, 6); SHA_RND2(E,A,B,C,D, 4);
|
||||
SHA_MIX( 5, 2, 13, 7); SHA_RND2(D,E,A,B,C, 5);
|
||||
SHA_MIX( 6, 3, 14, 8); SHA_RND2(C,D,E,A,B, 6);
|
||||
SHA_MIX( 7, 4, 15, 9); SHA_RND2(B,C,D,E,A, 7);
|
||||
SHA_MIX( 8, 5, 0, 10); SHA_RND2(A,B,C,D,E, 8);
|
||||
SHA_MIX( 9, 6, 1, 11); SHA_RND2(E,A,B,C,D, 9);
|
||||
SHA_MIX(10, 7, 2, 12); SHA_RND2(D,E,A,B,C,10);
|
||||
SHA_MIX(11, 8, 3, 13); SHA_RND2(C,D,E,A,B,11);
|
||||
SHA_MIX(12, 9, 4, 14); SHA_RND2(B,C,D,E,A,12);
|
||||
SHA_MIX(13, 10, 5, 15); SHA_RND2(A,B,C,D,E,13);
|
||||
SHA_MIX(14, 11, 6, 0); SHA_RND2(E,A,B,C,D,14);
|
||||
SHA_MIX(15, 12, 7, 1); SHA_RND2(D,E,A,B,C,15);
|
||||
|
||||
SHA_MIX( 0, 13, 8, 2); SHA_RND2(C,D,E,A,B, 0);
|
||||
SHA_MIX( 1, 14, 9, 3); SHA_RND2(B,C,D,E,A, 1);
|
||||
SHA_MIX( 2, 15, 10, 4); SHA_RND2(A,B,C,D,E, 2);
|
||||
SHA_MIX( 3, 0, 11, 5); SHA_RND2(E,A,B,C,D, 3);
|
||||
SHA_MIX( 4, 1, 12, 6); SHA_RND2(D,E,A,B,C, 4);
|
||||
SHA_MIX( 5, 2, 13, 7); SHA_RND2(C,D,E,A,B, 5);
|
||||
SHA_MIX( 6, 3, 14, 8); SHA_RND2(B,C,D,E,A, 6);
|
||||
SHA_MIX( 7, 4, 15, 9); SHA_RND2(A,B,C,D,E, 7);
|
||||
|
||||
SHA_MIX( 8, 5, 0, 10); SHA_RND3(E,A,B,C,D, 8);
|
||||
SHA_MIX( 9, 6, 1, 11); SHA_RND3(D,E,A,B,C, 9);
|
||||
SHA_MIX(10, 7, 2, 12); SHA_RND3(C,D,E,A,B,10);
|
||||
SHA_MIX(11, 8, 3, 13); SHA_RND3(B,C,D,E,A,11);
|
||||
SHA_MIX(12, 9, 4, 14); SHA_RND3(A,B,C,D,E,12);
|
||||
SHA_MIX(13, 10, 5, 15); SHA_RND3(E,A,B,C,D,13);
|
||||
SHA_MIX(14, 11, 6, 0); SHA_RND3(D,E,A,B,C,14);
|
||||
SHA_MIX(15, 12, 7, 1); SHA_RND3(C,D,E,A,B,15);
|
||||
|
||||
SHA_MIX( 0, 13, 8, 2); SHA_RND3(B,C,D,E,A, 0);
|
||||
SHA_MIX( 1, 14, 9, 3); SHA_RND3(A,B,C,D,E, 1);
|
||||
SHA_MIX( 2, 15, 10, 4); SHA_RND3(E,A,B,C,D, 2);
|
||||
SHA_MIX( 3, 0, 11, 5); SHA_RND3(D,E,A,B,C, 3);
|
||||
SHA_MIX( 4, 1, 12, 6); SHA_RND3(C,D,E,A,B, 4);
|
||||
SHA_MIX( 5, 2, 13, 7); SHA_RND3(B,C,D,E,A, 5);
|
||||
SHA_MIX( 6, 3, 14, 8); SHA_RND3(A,B,C,D,E, 6);
|
||||
SHA_MIX( 7, 4, 15, 9); SHA_RND3(E,A,B,C,D, 7);
|
||||
SHA_MIX( 8, 5, 0, 10); SHA_RND3(D,E,A,B,C, 8);
|
||||
SHA_MIX( 9, 6, 1, 11); SHA_RND3(C,D,E,A,B, 9);
|
||||
SHA_MIX(10, 7, 2, 12); SHA_RND3(B,C,D,E,A,10);
|
||||
SHA_MIX(11, 8, 3, 13); SHA_RND3(A,B,C,D,E,11);
|
||||
|
||||
SHA_MIX(12, 9, 4, 14); SHA_RND4(E,A,B,C,D,12);
|
||||
SHA_MIX(13, 10, 5, 15); SHA_RND4(D,E,A,B,C,13);
|
||||
SHA_MIX(14, 11, 6, 0); SHA_RND4(C,D,E,A,B,14);
|
||||
SHA_MIX(15, 12, 7, 1); SHA_RND4(B,C,D,E,A,15);
|
||||
|
||||
SHA_MIX( 0, 13, 8, 2); SHA_RND4(A,B,C,D,E, 0);
|
||||
SHA_MIX( 1, 14, 9, 3); SHA_RND4(E,A,B,C,D, 1);
|
||||
SHA_MIX( 2, 15, 10, 4); SHA_RND4(D,E,A,B,C, 2);
|
||||
SHA_MIX( 3, 0, 11, 5); SHA_RND4(C,D,E,A,B, 3);
|
||||
SHA_MIX( 4, 1, 12, 6); SHA_RND4(B,C,D,E,A, 4);
|
||||
SHA_MIX( 5, 2, 13, 7); SHA_RND4(A,B,C,D,E, 5);
|
||||
SHA_MIX( 6, 3, 14, 8); SHA_RND4(E,A,B,C,D, 6);
|
||||
SHA_MIX( 7, 4, 15, 9); SHA_RND4(D,E,A,B,C, 7);
|
||||
SHA_MIX( 8, 5, 0, 10); SHA_RND4(C,D,E,A,B, 8);
|
||||
SHA_MIX( 9, 6, 1, 11); SHA_RND4(B,C,D,E,A, 9);
|
||||
SHA_MIX(10, 7, 2, 12); SHA_RND4(A,B,C,D,E,10);
|
||||
SHA_MIX(11, 8, 3, 13); SHA_RND4(E,A,B,C,D,11);
|
||||
SHA_MIX(12, 9, 4, 14); SHA_RND4(D,E,A,B,C,12);
|
||||
SHA_MIX(13, 10, 5, 15); SHA_RND4(C,D,E,A,B,13);
|
||||
SHA_MIX(14, 11, 6, 0); SHA_RND4(B,C,D,E,A,14);
|
||||
SHA_MIX(15, 12, 7, 1); SHA_RND4(A,B,C,D,E,15);
|
||||
|
||||
XH(0) += A;
|
||||
XH(1) += B;
|
||||
XH(2) += C;
|
||||
XH(3) += D;
|
||||
XH(4) += E;
|
||||
}
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,72 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
/* Simple class for computing SHA1. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
//#include "mozilla/Types.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef MFBT_API
|
||||
#undef MFBT_API
|
||||
#endif
|
||||
|
||||
#define MFBT_API
|
||||
|
||||
namespace se {
|
||||
|
||||
/**
|
||||
* This class computes the SHA1 hash of a byte sequence, or of the concatenation
|
||||
* of multiple sequences. For example, computing the SHA1 of two sequences of
|
||||
* bytes could be done as follows:
|
||||
*
|
||||
* void SHA1(const uint8_t* buf1, uint32_t size1,
|
||||
* const uint8_t* buf2, uint32_t size2,
|
||||
* SHA1Sum::Hash& hash)
|
||||
* {
|
||||
* SHA1Sum s;
|
||||
* s.update(buf1, size1);
|
||||
* s.update(buf2, size2);
|
||||
* s.finish(hash);
|
||||
* }
|
||||
*
|
||||
* The finish method may only be called once and cannot be followed by calls
|
||||
* to update.
|
||||
*/
|
||||
class SHA1Sum
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t mW[16]; /* input buffer */
|
||||
uint8_t mB[64];
|
||||
} mU;
|
||||
uint64_t mSize; /* count of hashed bytes. */
|
||||
unsigned mH[22]; /* 5 state variables, 16 tmp values, 1 extra */
|
||||
bool mDone;
|
||||
|
||||
public:
|
||||
MFBT_API SHA1Sum();
|
||||
|
||||
static const size_t kHashSize = 20;
|
||||
typedef uint8_t Hash[kHashSize];
|
||||
|
||||
/* Add len bytes of dataIn to the data sequence being hashed. */
|
||||
MFBT_API void update(const void* aData, uint32_t aLength);
|
||||
|
||||
/* Compute the final hash of all data into hashOut. */
|
||||
MFBT_API void finish(SHA1Sum::Hash& aHashOut);
|
||||
};
|
||||
|
||||
} /* namespace se */
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#ifndef SRC_BASE64_H_
|
||||
#define SRC_BASE64_H_
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace node {
|
||||
//// Base 64 ////
|
||||
#define base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4)
|
||||
|
||||
|
||||
// Doesn't check for padding at the end. Can be 1-2 bytes over.
|
||||
static inline size_t base64_decoded_size_fast(size_t size) {
|
||||
size_t remainder = size % 4;
|
||||
|
||||
size = (size / 4) * 3;
|
||||
if (remainder) {
|
||||
if (size == 0 && remainder == 1) {
|
||||
// special case: 1-byte input cannot be decoded
|
||||
size = 0;
|
||||
} else {
|
||||
// non-padded input, add 1 or 2 extra bytes
|
||||
size += 1 + (remainder == 3);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
template <typename TypeName>
|
||||
size_t base64_decoded_size(const TypeName* src, size_t size) {
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
if (src[size - 1] == '=')
|
||||
size--;
|
||||
if (size > 0 && src[size - 1] == '=')
|
||||
size--;
|
||||
|
||||
return base64_decoded_size_fast(size);
|
||||
}
|
||||
|
||||
|
||||
extern const int8_t unbase64_table[256];
|
||||
|
||||
|
||||
#define unbase64(x) \
|
||||
static_cast<uint8_t>(unbase64_table[static_cast<uint8_t>(x)])
|
||||
|
||||
|
||||
template <typename TypeName>
|
||||
bool base64_decode_group_slow(char* const dst, const size_t dstlen,
|
||||
const TypeName* const src, const size_t srclen,
|
||||
size_t* const i, size_t* const k) {
|
||||
uint8_t hi;
|
||||
uint8_t lo;
|
||||
#define V(expr) \
|
||||
for (;;) { \
|
||||
const uint8_t c = src[*i]; \
|
||||
lo = unbase64(c); \
|
||||
*i += 1; \
|
||||
if (lo < 64) \
|
||||
break; /* Legal character. */ \
|
||||
if (c == '=' || *i >= srclen) \
|
||||
return false; /* Stop decoding. */ \
|
||||
} \
|
||||
expr; \
|
||||
if (*i >= srclen) \
|
||||
return false; \
|
||||
if (*k >= dstlen) \
|
||||
return false; \
|
||||
hi = lo;
|
||||
V(/* Nothing. */);
|
||||
V(dst[(*k)++] = ((hi & 0x3F) << 2) | ((lo & 0x30) >> 4));
|
||||
V(dst[(*k)++] = ((hi & 0x0F) << 4) | ((lo & 0x3C) >> 2));
|
||||
V(dst[(*k)++] = ((hi & 0x03) << 6) | ((lo & 0x3F) >> 0));
|
||||
#undef V
|
||||
return true; // Continue decoding.
|
||||
}
|
||||
|
||||
|
||||
template <typename TypeName>
|
||||
size_t base64_decode_fast(char* const dst, const size_t dstlen,
|
||||
const TypeName* const src, const size_t srclen,
|
||||
const size_t decoded_size) {
|
||||
const size_t available = dstlen < decoded_size ? dstlen : decoded_size;
|
||||
const size_t max_k = available / 3 * 3;
|
||||
size_t max_i = srclen / 4 * 4;
|
||||
size_t i = 0;
|
||||
size_t k = 0;
|
||||
while (i < max_i && k < max_k) {
|
||||
const uint32_t v =
|
||||
unbase64(src[i + 0]) << 24 |
|
||||
unbase64(src[i + 1]) << 16 |
|
||||
unbase64(src[i + 2]) << 8 |
|
||||
unbase64(src[i + 3]);
|
||||
// If MSB is set, input contains whitespace or is not valid base64.
|
||||
if (v & 0x80808080) {
|
||||
if (!base64_decode_group_slow(dst, dstlen, src, srclen, &i, &k))
|
||||
return k;
|
||||
max_i = i + (srclen - i) / 4 * 4; // Align max_i again.
|
||||
} else {
|
||||
dst[k + 0] = ((v >> 22) & 0xFC) | ((v >> 20) & 0x03);
|
||||
dst[k + 1] = ((v >> 12) & 0xF0) | ((v >> 10) & 0x0F);
|
||||
dst[k + 2] = ((v >> 2) & 0xC0) | ((v >> 0) & 0x3F);
|
||||
i += 4;
|
||||
k += 3;
|
||||
}
|
||||
}
|
||||
if (i < srclen && k < dstlen) {
|
||||
base64_decode_group_slow(dst, dstlen, src, srclen, &i, &k);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
|
||||
template <typename TypeName>
|
||||
size_t base64_decode(char* const dst, const size_t dstlen,
|
||||
const TypeName* const src, const size_t srclen) {
|
||||
const size_t decoded_size = base64_decoded_size(src, srclen);
|
||||
return base64_decode_fast(dst, dstlen, src, srclen, decoded_size);
|
||||
}
|
||||
|
||||
static size_t base64_encode(const char* src,
|
||||
size_t slen,
|
||||
char* dst,
|
||||
size_t dlen) {
|
||||
// We know how much we'll write, just make sure that there's space.
|
||||
CHECK(dlen >= base64_encoded_size(slen) &&
|
||||
"not enough space provided for base64 encode");
|
||||
|
||||
dlen = base64_encoded_size(slen);
|
||||
|
||||
unsigned a;
|
||||
unsigned b;
|
||||
unsigned c;
|
||||
unsigned i;
|
||||
unsigned k;
|
||||
unsigned n;
|
||||
|
||||
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
i = 0;
|
||||
k = 0;
|
||||
n = slen / 3 * 3;
|
||||
|
||||
while (i < n) {
|
||||
a = src[i + 0] & 0xff;
|
||||
b = src[i + 1] & 0xff;
|
||||
c = src[i + 2] & 0xff;
|
||||
|
||||
dst[k + 0] = table[a >> 2];
|
||||
dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
|
||||
dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)];
|
||||
dst[k + 3] = table[c & 0x3f];
|
||||
|
||||
i += 3;
|
||||
k += 4;
|
||||
}
|
||||
|
||||
if (n != slen) {
|
||||
switch (slen - n) {
|
||||
case 1:
|
||||
a = src[i + 0] & 0xff;
|
||||
dst[k + 0] = table[a >> 2];
|
||||
dst[k + 1] = table[(a & 3) << 4];
|
||||
dst[k + 2] = '=';
|
||||
dst[k + 3] = '=';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
a = src[i + 0] & 0xff;
|
||||
b = src[i + 1] & 0xff;
|
||||
dst[k + 0] = table[a >> 2];
|
||||
dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
|
||||
dst[k + 2] = table[(b & 0x0f) << 2];
|
||||
dst[k + 3] = '=';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dlen;
|
||||
}
|
||||
} // namespace node
|
||||
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#endif // SRC_BASE64_H_
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "env.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "uv.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace node {
|
||||
|
||||
void Environment::Start(int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv,
|
||||
bool start_profiler_idle_notifier)
|
||||
{
|
||||
HandleScope handle_scope(isolate());
|
||||
Context::Scope context_scope(context());
|
||||
|
||||
// uv_check_init(event_loop(), immediate_check_handle());
|
||||
// uv_unref(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
|
||||
//
|
||||
// uv_idle_init(event_loop(), immediate_idle_handle());
|
||||
//
|
||||
// // Inform V8's CPU profiler when we're idle. The profiler is sampling-based
|
||||
// // but not all samples are created equal; mark the wall clock time spent in
|
||||
// // epoll_wait() and friends so profiling tools can filter it out. The samples
|
||||
// // still end up in v8.log but with state=IDLE rather than state=EXTERNAL.
|
||||
// // REFINE(bnoordhuis) Depends on a libuv implementation detail that we should
|
||||
// // probably fortify in the API contract, namely that the last started prepare
|
||||
// // or check watcher runs first. It's not 100% foolproof; if an add-on starts
|
||||
// // a prepare or check watcher after us, any samples attributed to its callback
|
||||
// // will be recorded with state=IDLE.
|
||||
// uv_prepare_init(event_loop(), &idle_prepare_handle_);
|
||||
// uv_check_init(event_loop(), &idle_check_handle_);
|
||||
// uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
|
||||
// uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
|
||||
//
|
||||
// uv_timer_init(event_loop(), destroy_ids_timer_handle());
|
||||
//
|
||||
// auto close_and_finish = [](Environment* env, uv_handle_t* handle, void* arg) {
|
||||
// handle->data = env;
|
||||
//
|
||||
// uv_close(handle, [](uv_handle_t* handle) {
|
||||
// static_cast<Environment*>(handle->data)->FinishHandleCleanup(handle);
|
||||
// });
|
||||
// };
|
||||
//
|
||||
// RegisterHandleCleanup(
|
||||
// reinterpret_cast<uv_handle_t*>(immediate_check_handle()),
|
||||
// close_and_finish,
|
||||
// nullptr);
|
||||
// RegisterHandleCleanup(
|
||||
// reinterpret_cast<uv_handle_t*>(immediate_idle_handle()),
|
||||
// close_and_finish,
|
||||
// nullptr);
|
||||
// RegisterHandleCleanup(
|
||||
// reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_),
|
||||
// close_and_finish,
|
||||
// nullptr);
|
||||
// RegisterHandleCleanup(
|
||||
// reinterpret_cast<uv_handle_t*>(&idle_check_handle_),
|
||||
// close_and_finish,
|
||||
// nullptr);
|
||||
|
||||
if (start_profiler_idle_notifier) {
|
||||
StartProfilerIdleNotifier();
|
||||
}
|
||||
|
||||
auto process_template = FunctionTemplate::New(isolate());
|
||||
process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate(), "process"));
|
||||
|
||||
auto process_object =
|
||||
process_template->GetFunction(context()).ToLocalChecked()->NewInstance(context()).ToLocalChecked();
|
||||
set_process_object(process_object);
|
||||
|
||||
SetupProcessObject(this, argc, argv, exec_argc, exec_argv);
|
||||
// LoadAsyncWrapperInfo(this);
|
||||
}
|
||||
|
||||
void Environment::AssignToContext(v8::Local<v8::Context> context)
|
||||
{
|
||||
context->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, this);
|
||||
}
|
||||
|
||||
void Environment::CleanupHandles()
|
||||
{
|
||||
// while (HandleCleanup* hc = handle_cleanup_queue_.PopFront()) {
|
||||
// handle_cleanup_waiting_++;
|
||||
// hc->cb_(this, hc->handle_, hc->arg_);
|
||||
// delete hc;
|
||||
// }
|
||||
//
|
||||
// while (handle_cleanup_waiting_ != 0)
|
||||
// uv_run(event_loop(), UV_RUN_ONCE);
|
||||
//
|
||||
// while (handle_cleanup_waiting_ != 0)
|
||||
// uv_run(event_loop(), UV_RUN_ONCE);
|
||||
}
|
||||
|
||||
void Environment::StartProfilerIdleNotifier()
|
||||
{
|
||||
// uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) {
|
||||
// Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle);
|
||||
// env->isolate()->GetCpuProfiler()->SetIdle(true);
|
||||
// });
|
||||
//
|
||||
// uv_check_start(&idle_check_handle_, [](uv_check_t* handle) {
|
||||
// Environment* env = ContainerOf(&Environment::idle_check_handle_, handle);
|
||||
// env->isolate()->GetCpuProfiler()->SetIdle(false);
|
||||
// });
|
||||
}
|
||||
|
||||
void Environment::StopProfilerIdleNotifier()
|
||||
{
|
||||
// uv_prepare_stop(&idle_prepare_handle_);
|
||||
// uv_check_stop(&idle_check_handle_);
|
||||
}
|
||||
|
||||
} // namespace node {
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,629 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_agent.h"
|
||||
|
||||
#include "v8.h"
|
||||
#include "uv.h"
|
||||
#include "util.h"
|
||||
#include "node.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
// Pick an index that's hopefully out of the way when we're embedded inside
|
||||
// another application. Performance-wise or memory-wise it doesn't matter:
|
||||
// Context::SetAlignedPointerInEmbedderData() is backed by a FixedArray,
|
||||
// worst case we pay a one-time penalty for resizing the array.
|
||||
#ifndef NODE_CONTEXT_EMBEDDER_DATA_INDEX
|
||||
#define NODE_CONTEXT_EMBEDDER_DATA_INDEX 32
|
||||
#endif
|
||||
|
||||
|
||||
// PER_ISOLATE_* macros: We have a lot of per-isolate properties
|
||||
// and adding and maintaining their getters and setters by hand would be
|
||||
// difficult so let's make the preprocessor generate them for us.
|
||||
//
|
||||
// In each macro, `V` is expected to be the name of a macro or function which
|
||||
// accepts the number of arguments provided in each tuple in the macro body,
|
||||
// typically two. The named function will be invoked against each tuple.
|
||||
//
|
||||
// Make sure that any macro V defined for use with the PER_ISOLATE_* macros is
|
||||
// undefined again after use.
|
||||
|
||||
// Private symbols are per-isolate primitives but Environment proxies them
|
||||
// for the sake of convenience. Strings should be ASCII-only and have a
|
||||
// "node:" prefix to avoid name clashes with third-party code.
|
||||
#define PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) \
|
||||
V(alpn_buffer_private_symbol, "node:alpnBuffer") \
|
||||
V(arrow_message_private_symbol, "node:arrowMessage") \
|
||||
V(contextify_context_private_symbol, "node:contextify:context") \
|
||||
V(contextify_global_private_symbol, "node:contextify:global") \
|
||||
V(inspector_delegate_private_symbol, "node:inspector:delegate") \
|
||||
V(decorated_private_symbol, "node:decorated") \
|
||||
V(npn_buffer_private_symbol, "node:npnBuffer") \
|
||||
V(processed_private_symbol, "node:processed") \
|
||||
V(selected_npn_buffer_private_symbol, "node:selectedNpnBuffer") \
|
||||
|
||||
// Strings are per-isolate primitives but Environment proxies them
|
||||
// for the sake of convenience. Strings should be ASCII-only.
|
||||
#define PER_ISOLATE_STRING_PROPERTIES(V) \
|
||||
V(address_string, "address") \
|
||||
V(args_string, "args") \
|
||||
V(async, "async") \
|
||||
V(buffer_string, "buffer") \
|
||||
V(bytes_string, "bytes") \
|
||||
V(bytes_parsed_string, "bytesParsed") \
|
||||
V(bytes_read_string, "bytesRead") \
|
||||
V(cached_data_string, "cachedData") \
|
||||
V(cached_data_produced_string, "cachedDataProduced") \
|
||||
V(cached_data_rejected_string, "cachedDataRejected") \
|
||||
V(callback_string, "callback") \
|
||||
V(change_string, "change") \
|
||||
V(channel_string, "channel") \
|
||||
V(oncertcb_string, "oncertcb") \
|
||||
V(onclose_string, "_onclose") \
|
||||
V(code_string, "code") \
|
||||
V(configurable_string, "configurable") \
|
||||
V(cwd_string, "cwd") \
|
||||
V(dest_string, "dest") \
|
||||
V(detached_string, "detached") \
|
||||
V(disposed_string, "_disposed") \
|
||||
V(dns_a_string, "A") \
|
||||
V(dns_aaaa_string, "AAAA") \
|
||||
V(dns_cname_string, "CNAME") \
|
||||
V(dns_mx_string, "MX") \
|
||||
V(dns_naptr_string, "NAPTR") \
|
||||
V(dns_ns_string, "NS") \
|
||||
V(dns_ptr_string, "PTR") \
|
||||
V(dns_soa_string, "SOA") \
|
||||
V(dns_srv_string, "SRV") \
|
||||
V(dns_txt_string, "TXT") \
|
||||
V(domain_string, "domain") \
|
||||
V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \
|
||||
V(exchange_string, "exchange") \
|
||||
V(enumerable_string, "enumerable") \
|
||||
V(idle_string, "idle") \
|
||||
V(irq_string, "irq") \
|
||||
V(encoding_string, "encoding") \
|
||||
V(enter_string, "enter") \
|
||||
V(entries_string, "entries") \
|
||||
V(env_pairs_string, "envPairs") \
|
||||
V(errno_string, "errno") \
|
||||
V(error_string, "error") \
|
||||
V(events_string, "_events") \
|
||||
V(exiting_string, "_exiting") \
|
||||
V(exit_code_string, "exitCode") \
|
||||
V(exit_string, "exit") \
|
||||
V(expire_string, "expire") \
|
||||
V(exponent_string, "exponent") \
|
||||
V(exports_string, "exports") \
|
||||
V(ext_key_usage_string, "ext_key_usage") \
|
||||
V(external_stream_string, "_externalStream") \
|
||||
V(family_string, "family") \
|
||||
V(fatal_exception_string, "_fatalException") \
|
||||
V(fd_string, "fd") \
|
||||
V(file_string, "file") \
|
||||
V(fingerprint_string, "fingerprint") \
|
||||
V(flags_string, "flags") \
|
||||
V(get_string, "get") \
|
||||
V(get_data_clone_error_string, "_getDataCloneError") \
|
||||
V(get_shared_array_buffer_id_string, "_getSharedArrayBufferId") \
|
||||
V(gid_string, "gid") \
|
||||
V(handle_string, "handle") \
|
||||
V(homedir_string, "homedir") \
|
||||
V(hostmaster_string, "hostmaster") \
|
||||
V(ignore_string, "ignore") \
|
||||
V(immediate_callback_string, "_immediateCallback") \
|
||||
V(infoaccess_string, "infoAccess") \
|
||||
V(inherit_string, "inherit") \
|
||||
V(input_string, "input") \
|
||||
V(internal_string, "internal") \
|
||||
V(ipv4_string, "IPv4") \
|
||||
V(ipv6_string, "IPv6") \
|
||||
V(isalive_string, "isAlive") \
|
||||
V(isclosing_string, "isClosing") \
|
||||
V(issuer_string, "issuer") \
|
||||
V(issuercert_string, "issuerCertificate") \
|
||||
V(kill_signal_string, "killSignal") \
|
||||
V(length_string, "length") \
|
||||
V(mac_string, "mac") \
|
||||
V(max_buffer_string, "maxBuffer") \
|
||||
V(message_string, "message") \
|
||||
V(minttl_string, "minttl") \
|
||||
V(model_string, "model") \
|
||||
V(modulus_string, "modulus") \
|
||||
V(name_string, "name") \
|
||||
V(netmask_string, "netmask") \
|
||||
V(nice_string, "nice") \
|
||||
V(nsname_string, "nsname") \
|
||||
V(ocsp_request_string, "OCSPRequest") \
|
||||
V(onchange_string, "onchange") \
|
||||
V(onclienthello_string, "onclienthello") \
|
||||
V(oncomplete_string, "oncomplete") \
|
||||
V(onconnection_string, "onconnection") \
|
||||
V(ondone_string, "ondone") \
|
||||
V(onerror_string, "onerror") \
|
||||
V(onexit_string, "onexit") \
|
||||
V(onhandshakedone_string, "onhandshakedone") \
|
||||
V(onhandshakestart_string, "onhandshakestart") \
|
||||
V(onmessage_string, "onmessage") \
|
||||
V(onnewsession_string, "onnewsession") \
|
||||
V(onnewsessiondone_string, "onnewsessiondone") \
|
||||
V(onocspresponse_string, "onocspresponse") \
|
||||
V(onread_string, "onread") \
|
||||
V(onreadstart_string, "onreadstart") \
|
||||
V(onreadstop_string, "onreadstop") \
|
||||
V(onselect_string, "onselect") \
|
||||
V(onshutdown_string, "onshutdown") \
|
||||
V(onsignal_string, "onsignal") \
|
||||
V(onstop_string, "onstop") \
|
||||
V(onwrite_string, "onwrite") \
|
||||
V(output_string, "output") \
|
||||
V(order_string, "order") \
|
||||
V(owner_string, "owner") \
|
||||
V(parse_error_string, "Parse Error") \
|
||||
V(path_string, "path") \
|
||||
V(pbkdf2_error_string, "PBKDF2 Error") \
|
||||
V(pid_string, "pid") \
|
||||
V(pipe_string, "pipe") \
|
||||
V(port_string, "port") \
|
||||
V(preference_string, "preference") \
|
||||
V(priority_string, "priority") \
|
||||
V(produce_cached_data_string, "produceCachedData") \
|
||||
V(raw_string, "raw") \
|
||||
V(read_host_object_string, "_readHostObject") \
|
||||
V(readable_string, "readable") \
|
||||
V(received_shutdown_string, "receivedShutdown") \
|
||||
V(refresh_string, "refresh") \
|
||||
V(regexp_string, "regexp") \
|
||||
V(rename_string, "rename") \
|
||||
V(replacement_string, "replacement") \
|
||||
V(retry_string, "retry") \
|
||||
V(serial_string, "serial") \
|
||||
V(scopeid_string, "scopeid") \
|
||||
V(sent_shutdown_string, "sentShutdown") \
|
||||
V(serial_number_string, "serialNumber") \
|
||||
V(service_string, "service") \
|
||||
V(servername_string, "servername") \
|
||||
V(session_id_string, "sessionId") \
|
||||
V(set_string, "set") \
|
||||
V(shell_string, "shell") \
|
||||
V(signal_string, "signal") \
|
||||
V(size_string, "size") \
|
||||
V(sni_context_err_string, "Invalid SNI context") \
|
||||
V(sni_context_string, "sni_context") \
|
||||
V(speed_string, "speed") \
|
||||
V(stack_string, "stack") \
|
||||
V(status_string, "status") \
|
||||
V(stdio_string, "stdio") \
|
||||
V(subject_string, "subject") \
|
||||
V(subjectaltname_string, "subjectaltname") \
|
||||
V(sys_string, "sys") \
|
||||
V(syscall_string, "syscall") \
|
||||
V(tick_callback_string, "_tickCallback") \
|
||||
V(tick_domain_cb_string, "_tickDomainCallback") \
|
||||
V(ticketkeycallback_string, "onticketkeycallback") \
|
||||
V(timeout_string, "timeout") \
|
||||
V(times_string, "times") \
|
||||
V(tls_ticket_string, "tlsTicket") \
|
||||
V(ttl_string, "ttl") \
|
||||
V(type_string, "type") \
|
||||
V(uid_string, "uid") \
|
||||
V(unknown_string, "<unknown>") \
|
||||
V(user_string, "user") \
|
||||
V(username_string, "username") \
|
||||
V(valid_from_string, "valid_from") \
|
||||
V(valid_to_string, "valid_to") \
|
||||
V(value_string, "value") \
|
||||
V(verify_error_string, "verifyError") \
|
||||
V(version_string, "version") \
|
||||
V(weight_string, "weight") \
|
||||
V(windows_verbatim_arguments_string, "windowsVerbatimArguments") \
|
||||
V(wrap_string, "wrap") \
|
||||
V(writable_string, "writable") \
|
||||
V(write_host_object_string, "_writeHostObject") \
|
||||
V(write_queue_size_string, "writeQueueSize") \
|
||||
V(x_forwarded_string, "x-forwarded-for") \
|
||||
V(zero_return_string, "ZERO_RETURN") \
|
||||
|
||||
|
||||
|
||||
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
|
||||
V(as_external, v8::External) \
|
||||
V(async_hooks_destroy_function, v8::Function) \
|
||||
V(async_hooks_init_function, v8::Function) \
|
||||
V(async_hooks_before_function, v8::Function) \
|
||||
V(async_hooks_after_function, v8::Function) \
|
||||
V(binding_cache_object, v8::Object) \
|
||||
V(buffer_constructor_function, v8::Function) \
|
||||
V(buffer_prototype_object, v8::Object) \
|
||||
V(context, v8::Context) \
|
||||
V(domain_array, v8::Array) \
|
||||
V(domains_stack_array, v8::Array) \
|
||||
V(jsstream_constructor_template, v8::FunctionTemplate) \
|
||||
V(module_load_list_array, v8::Array) \
|
||||
V(pbkdf2_constructor_template, v8::ObjectTemplate) \
|
||||
V(pipe_constructor_template, v8::FunctionTemplate) \
|
||||
V(process_object, v8::Object) \
|
||||
V(promise_reject_function, v8::Function) \
|
||||
V(promise_wrap_template, v8::ObjectTemplate) \
|
||||
V(push_values_to_array_function, v8::Function) \
|
||||
V(randombytes_constructor_template, v8::ObjectTemplate) \
|
||||
V(script_context_constructor_template, v8::FunctionTemplate) \
|
||||
V(script_data_constructor_function, v8::Function) \
|
||||
V(secure_context_constructor_template, v8::FunctionTemplate) \
|
||||
V(tcp_constructor_template, v8::FunctionTemplate) \
|
||||
V(tick_callback_function, v8::Function) \
|
||||
V(tls_wrap_constructor_function, v8::Function) \
|
||||
V(tls_wrap_constructor_template, v8::FunctionTemplate) \
|
||||
V(tty_constructor_template, v8::FunctionTemplate) \
|
||||
V(udp_constructor_function, v8::Function) \
|
||||
V(url_constructor_function, v8::Function) \
|
||||
V(write_wrap_constructor_function, v8::Function) \
|
||||
|
||||
|
||||
class IsolateData {
|
||||
public:
|
||||
inline IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop,
|
||||
uint32_t* zero_fill_field = nullptr);
|
||||
inline uv_loop_t* event_loop() const;
|
||||
inline uint32_t* zero_fill_field() const;
|
||||
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
inline v8::Local<TypeName> PropertyName(v8::Isolate* isolate) const;
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
private:
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
v8::Eternal<TypeName> PropertyName ## _;
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
uv_loop_t* const event_loop_;
|
||||
uint32_t* const zero_fill_field_;
|
||||
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(IsolateData);
|
||||
};
|
||||
|
||||
class Environment
|
||||
{
|
||||
public:
|
||||
|
||||
static const int kContextEmbedderDataIndex = NODE_CONTEXT_EMBEDDER_DATA_INDEX;
|
||||
|
||||
static inline Environment* GetCurrent(v8::Isolate* isolate);
|
||||
static inline Environment* GetCurrent(v8::Local<v8::Context> context);
|
||||
static inline Environment* GetCurrent(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
|
||||
template <typename T>
|
||||
static inline Environment* GetCurrent(const v8::PropertyCallbackInfo<T>& info);
|
||||
|
||||
inline Environment(IsolateData* isolate_data, v8::Local<v8::Context> context);
|
||||
inline ~Environment();
|
||||
|
||||
void Start(int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv,
|
||||
bool start_profiler_idle_notifier);
|
||||
void AssignToContext(v8::Local<v8::Context> context);
|
||||
void CleanupHandles();
|
||||
|
||||
void StartProfilerIdleNotifier();
|
||||
void StopProfilerIdleNotifier();
|
||||
|
||||
inline v8::Isolate* isolate() const
|
||||
{
|
||||
return isolate_;
|
||||
}
|
||||
|
||||
inline IsolateData* isolate_data() const
|
||||
{
|
||||
return isolate_data_;
|
||||
}
|
||||
|
||||
inline uv_loop_t* event_loop() const
|
||||
{
|
||||
return isolate_data()->event_loop();
|
||||
}
|
||||
|
||||
inline inspector::Agent* inspector_agent() {
|
||||
return &inspector_agent_;
|
||||
}
|
||||
|
||||
// Strings and private symbols are shared across shared contexts
|
||||
// The getters simply proxy to the per-isolate primitive.
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
inline v8::Local<TypeName> PropertyName() const;
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
|
||||
#define V(PropertyName, TypeName) \
|
||||
inline v8::Local<TypeName> PropertyName() const; \
|
||||
inline void set_ ## PropertyName(v8::Local<TypeName> value);
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
|
||||
inline void ThrowError(const char* errmsg);
|
||||
inline void ThrowTypeError(const char* errmsg);
|
||||
inline void ThrowRangeError(const char* errmsg);
|
||||
inline void ThrowErrnoException(int errorno,
|
||||
const char* syscall = nullptr,
|
||||
const char* message = nullptr,
|
||||
const char* path = nullptr);
|
||||
inline void ThrowUVException(int errorno,
|
||||
const char* syscall = nullptr,
|
||||
const char* message = nullptr,
|
||||
const char* path = nullptr,
|
||||
const char* dest = nullptr);
|
||||
|
||||
inline v8::Local<v8::FunctionTemplate>
|
||||
NewFunctionTemplate(v8::FunctionCallback callback,
|
||||
v8::Local<v8::Signature> signature =
|
||||
v8::Local<v8::Signature>());
|
||||
|
||||
// Convenience methods for NewFunctionTemplate().
|
||||
inline void SetMethod(v8::Local<v8::Object> that,
|
||||
const char* name,
|
||||
v8::FunctionCallback callback);
|
||||
|
||||
class AsyncCallbackScope {
|
||||
public:
|
||||
AsyncCallbackScope() = delete;
|
||||
explicit AsyncCallbackScope(Environment* env);
|
||||
~AsyncCallbackScope();
|
||||
inline bool in_makecallback();
|
||||
|
||||
private:
|
||||
Environment* env_;
|
||||
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(AsyncCallbackScope);
|
||||
};
|
||||
|
||||
private:
|
||||
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
|
||||
const char* errmsg);
|
||||
|
||||
v8::Isolate* const isolate_;
|
||||
IsolateData* const isolate_data_;
|
||||
|
||||
#define V(PropertyName, TypeName) \
|
||||
v8::Persistent<TypeName> PropertyName ## _;
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
inspector::Agent inspector_agent_;
|
||||
size_t makecallback_cntr_;
|
||||
};
|
||||
|
||||
inline IsolateData::IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop,
|
||||
uint32_t* zero_fill_field) :
|
||||
|
||||
// Create string and private symbol properties as internalized one byte strings.
|
||||
//
|
||||
// Internalized because it makes property lookups a little faster and because
|
||||
// the string is created in the old space straight away. It's going to end up
|
||||
// in the old space sooner or later anyway but now it doesn't go through
|
||||
// v8::Eternal's new space handling first.
|
||||
//
|
||||
// One byte because our strings are ASCII and we can safely skip V8's UTF-8
|
||||
// decoding step. It's a one-time cost, but why pay it when you don't have to?
|
||||
#define V(PropertyName, StringValue) \
|
||||
PropertyName ## _( \
|
||||
isolate, \
|
||||
v8::Private::New( \
|
||||
isolate, \
|
||||
v8::String::NewFromOneByte( \
|
||||
isolate, \
|
||||
reinterpret_cast<const uint8_t*>(StringValue), \
|
||||
v8::NewStringType::kInternalized, \
|
||||
sizeof(StringValue) - 1).ToLocalChecked())),
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
|
||||
#undef V
|
||||
#define V(PropertyName, StringValue) \
|
||||
PropertyName ## _( \
|
||||
isolate, \
|
||||
v8::String::NewFromOneByte( \
|
||||
isolate, \
|
||||
reinterpret_cast<const uint8_t*>(StringValue), \
|
||||
v8::NewStringType::kInternalized, \
|
||||
sizeof(StringValue) - 1).ToLocalChecked()),
|
||||
PER_ISOLATE_STRING_PROPERTIES(V)
|
||||
#undef V
|
||||
event_loop_(event_loop), zero_fill_field_(zero_fill_field) {}
|
||||
|
||||
inline uv_loop_t* IsolateData::event_loop() const {
|
||||
return event_loop_;
|
||||
}
|
||||
|
||||
inline uint32_t* IsolateData::zero_fill_field() const {
|
||||
return zero_fill_field_;
|
||||
}
|
||||
|
||||
|
||||
inline Environment* Environment::GetCurrent(v8::Isolate* isolate) {
|
||||
return GetCurrent(isolate->GetCurrentContext());
|
||||
}
|
||||
|
||||
inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
|
||||
return static_cast<Environment*>(
|
||||
context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex));
|
||||
}
|
||||
|
||||
inline Environment* Environment::GetCurrent(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
CHECK(info.Data()->IsExternal());
|
||||
return static_cast<Environment*>(info.Data().As<v8::External>()->Value());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Environment* Environment::GetCurrent(
|
||||
const v8::PropertyCallbackInfo<T>& info) {
|
||||
CHECK(info.Data()->IsExternal());
|
||||
// XXX(bnoordhuis) Work around a g++ 4.9.2 template type inferrer bug
|
||||
// when the expression is written as info.Data().As<v8::External>().
|
||||
v8::Local<v8::Value> data = info.Data();
|
||||
return static_cast<Environment*>(data.As<v8::External>()->Value());
|
||||
}
|
||||
|
||||
inline Environment::Environment(IsolateData* isolate_data, v8::Local<v8::Context> context)
|
||||
: isolate_(context->GetIsolate()),
|
||||
isolate_data_(isolate_data),
|
||||
makecallback_cntr_(0),
|
||||
inspector_agent_(this),
|
||||
context_(context->GetIsolate(), context) {
|
||||
// We'll be creating new objects so make sure we've entered the context.
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
v8::Context::Scope context_scope(context);
|
||||
set_as_external(v8::External::New(isolate(), this));
|
||||
set_binding_cache_object(v8::Object::New(isolate()));
|
||||
set_module_load_list_array(v8::Array::New(isolate()));
|
||||
|
||||
AssignToContext(context);
|
||||
|
||||
//cjh destroy_ids_list_.reserve(512);
|
||||
}
|
||||
|
||||
inline Environment::~Environment() {
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
|
||||
context()->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex,
|
||||
nullptr);
|
||||
#define V(PropertyName, TypeName) PropertyName ## _.Reset();
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
|
||||
// delete[] heap_statistics_buffer_;
|
||||
// delete[] heap_space_statistics_buffer_;
|
||||
// delete[] http_parser_buffer_;
|
||||
}
|
||||
|
||||
inline void Environment::SetMethod(v8::Local<v8::Object> that,
|
||||
const char* name,
|
||||
v8::FunctionCallback callback) {
|
||||
v8::Local<v8::Function> function =
|
||||
NewFunctionTemplate(callback)->GetFunction(context()).ToLocalChecked();
|
||||
// kInternalized strings are created in the old space.
|
||||
const v8::NewStringType type = v8::NewStringType::kInternalized;
|
||||
v8::Local<v8::String> name_string =
|
||||
v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked();
|
||||
that->Set(context(), name_string, function);
|
||||
function->SetName(name_string); // NODE_SET_METHOD() compatibility.
|
||||
}
|
||||
|
||||
inline void Environment::ThrowError(const char* errmsg) {
|
||||
ThrowError(v8::Exception::Error, errmsg);
|
||||
}
|
||||
|
||||
inline void Environment::ThrowTypeError(const char* errmsg) {
|
||||
ThrowError(v8::Exception::TypeError, errmsg);
|
||||
}
|
||||
|
||||
inline void Environment::ThrowRangeError(const char* errmsg) {
|
||||
ThrowError(v8::Exception::RangeError, errmsg);
|
||||
}
|
||||
|
||||
inline void Environment::ThrowError(
|
||||
v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
|
||||
const char* errmsg) {
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
isolate()->ThrowException(fun(OneByteString(isolate(), errmsg)));
|
||||
}
|
||||
|
||||
inline void Environment::ThrowErrnoException(int errorno,
|
||||
const char* syscall,
|
||||
const char* message,
|
||||
const char* path) {
|
||||
isolate()->ThrowException(
|
||||
ErrnoException(isolate(), errorno, syscall, message, path));
|
||||
}
|
||||
|
||||
inline void Environment::ThrowUVException(int errorno,
|
||||
const char* syscall,
|
||||
const char* message,
|
||||
const char* path,
|
||||
const char* dest) {
|
||||
isolate()->ThrowException(
|
||||
UVException(isolate(), errorno, syscall, message, path, dest));
|
||||
}
|
||||
|
||||
inline v8::Local<v8::FunctionTemplate>
|
||||
Environment::NewFunctionTemplate(v8::FunctionCallback callback,
|
||||
v8::Local<v8::Signature> signature) {
|
||||
v8::Local<v8::External> external = as_external();
|
||||
return v8::FunctionTemplate::New(isolate(), callback, external, signature);
|
||||
}
|
||||
|
||||
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
inline \
|
||||
v8::Local<TypeName> IsolateData::PropertyName(v8::Isolate* isolate) const { \
|
||||
/* Strings are immutable so casting away const-ness here is okay. */ \
|
||||
return const_cast<IsolateData*>(this)->PropertyName ## _.Get(isolate); \
|
||||
}
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
inline v8::Local<TypeName> Environment::PropertyName() const { \
|
||||
return isolate_data()->PropertyName(isolate()); \
|
||||
}
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
#define V(PropertyName, TypeName) \
|
||||
inline v8::Local<TypeName> Environment::PropertyName() const { \
|
||||
return StrongPersistentToLocal(PropertyName ## _); \
|
||||
} \
|
||||
inline void Environment::set_ ## PropertyName(v8::Local<TypeName> value) { \
|
||||
PropertyName ## _.Reset(isolate(), value); \
|
||||
}
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
|
||||
inline Environment::AsyncCallbackScope::AsyncCallbackScope(Environment* env)
|
||||
: env_(env) {
|
||||
env_->makecallback_cntr_++;
|
||||
}
|
||||
|
||||
inline Environment::AsyncCallbackScope::~AsyncCallbackScope() {
|
||||
env_->makecallback_cntr_--;
|
||||
}
|
||||
|
||||
inline bool Environment::AsyncCallbackScope::in_makecallback() {
|
||||
return env_->makecallback_cntr_ > 1;
|
||||
}
|
||||
|
||||
|
||||
} // namespace node {
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,369 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Also update SONAME in the Makefile whenever you change these. */
|
||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||
#define HTTP_PARSER_VERSION_MINOR 7
|
||||
#define HTTP_PARSER_VERSION_PATCH 0
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
||||
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
|
||||
#include <BaseTsd.h>
|
||||
#include <stddef.h>
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
||||
* faster
|
||||
*/
|
||||
#ifndef HTTP_PARSER_STRICT
|
||||
# define HTTP_PARSER_STRICT 1
|
||||
#endif
|
||||
|
||||
/* Maximium header size allowed. If the macro is not defined
|
||||
* before including this header then the default is used. To
|
||||
* change the maximum header size, define the macro in the build
|
||||
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
|
||||
* the effective limit on the size of the header, define the macro
|
||||
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
|
||||
*/
|
||||
#ifndef HTTP_MAX_HEADER_SIZE
|
||||
# define HTTP_MAX_HEADER_SIZE (80*1024)
|
||||
#endif
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
typedef struct http_parser_settings http_parser_settings;
|
||||
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parser will
|
||||
* then halt execution.
|
||||
*
|
||||
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
||||
* returning '1' from on_headers_complete will tell the parser that it
|
||||
* should not expect a body. This is used when receiving a response to a
|
||||
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
||||
* chunked' headers that indicate the presence of a body.
|
||||
*
|
||||
* Returning `2` from on_headers_complete will tell parser that it should not
|
||||
* expect neither a body nor any futher responses on this connection. This is
|
||||
* useful for handling responses to a CONNECT request which may not contain
|
||||
* `Upgrade` or `Connection: upgrade` headers.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be called arbitrarily
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_url"
|
||||
* each providing just a few characters more data.
|
||||
*/
|
||||
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
||||
typedef int (*http_cb) (http_parser*);
|
||||
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* WebDAV */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
/* subversion */ \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(24, MSEARCH, M-SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
/* CalDAV */ \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
/* RFC-2068, section 19.6.1.2 */ \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
|
||||
enum http_method
|
||||
{
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||
|
||||
|
||||
/* Flag values for http_parser.flags field */
|
||||
enum flags
|
||||
{ F_CHUNKED = 1 << 0
|
||||
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
||||
, F_CONNECTION_CLOSE = 1 << 2
|
||||
, F_CONNECTION_UPGRADE = 1 << 3
|
||||
, F_TRAILING = 1 << 4
|
||||
, F_UPGRADE = 1 << 5
|
||||
, F_SKIPBODY = 1 << 6
|
||||
, F_CONTENTLENGTH = 1 << 7
|
||||
};
|
||||
|
||||
|
||||
/* Map for errno-related constants
|
||||
*
|
||||
* The provided argument should be a macro that takes 2 arguments.
|
||||
*/
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
/* No error */ \
|
||||
XX(OK, "success") \
|
||||
\
|
||||
/* Callback-related errors */ \
|
||||
XX(CB_message_begin, "the on_message_begin callback failed") \
|
||||
XX(CB_url, "the on_url callback failed") \
|
||||
XX(CB_header_field, "the on_header_field callback failed") \
|
||||
XX(CB_header_value, "the on_header_value callback failed") \
|
||||
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
||||
XX(CB_body, "the on_body callback failed") \
|
||||
XX(CB_message_complete, "the on_message_complete callback failed") \
|
||||
XX(CB_status, "the on_status callback failed") \
|
||||
XX(CB_chunk_header, "the on_chunk_header callback failed") \
|
||||
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
|
||||
\
|
||||
/* Parsing-related errors */ \
|
||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
||||
XX(HEADER_OVERFLOW, \
|
||||
"too many header bytes seen; overflow detected") \
|
||||
XX(CLOSED_CONNECTION, \
|
||||
"data received after completed connection: close message") \
|
||||
XX(INVALID_VERSION, "invalid HTTP version") \
|
||||
XX(INVALID_STATUS, "invalid HTTP status code") \
|
||||
XX(INVALID_METHOD, "invalid HTTP method") \
|
||||
XX(INVALID_URL, "invalid URL") \
|
||||
XX(INVALID_HOST, "invalid host") \
|
||||
XX(INVALID_PORT, "invalid port") \
|
||||
XX(INVALID_PATH, "invalid path") \
|
||||
XX(INVALID_QUERY_STRING, "invalid query string") \
|
||||
XX(INVALID_FRAGMENT, "invalid fragment") \
|
||||
XX(LF_EXPECTED, "LF character expected") \
|
||||
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
||||
XX(INVALID_CONTENT_LENGTH, \
|
||||
"invalid character in content-length header") \
|
||||
XX(UNEXPECTED_CONTENT_LENGTH, \
|
||||
"unexpected content-length header") \
|
||||
XX(INVALID_CHUNK_SIZE, \
|
||||
"invalid character in chunk size header") \
|
||||
XX(INVALID_CONSTANT, "invalid constant string") \
|
||||
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
||||
XX(STRICT, "strict mode assertion failed") \
|
||||
XX(PAUSED, "parser is paused") \
|
||||
XX(UNKNOWN, "an unknown error occurred")
|
||||
|
||||
|
||||
/* Define HPE_* values for each errno value above */
|
||||
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
||||
enum http_errno {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
};
|
||||
#undef HTTP_ERRNO_GEN
|
||||
|
||||
|
||||
/* Get an http_errno value from an http_parser */
|
||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
||||
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
unsigned int type : 2; /* enum http_parser_type */
|
||||
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
|
||||
unsigned int state : 7; /* enum state from http_parser.c */
|
||||
unsigned int header_state : 7; /* enum header_state from http_parser.c */
|
||||
unsigned int index : 7; /* index into current matcher */
|
||||
unsigned int lenient_http_headers : 1;
|
||||
|
||||
uint32_t nread; /* # bytes read in various scenarios */
|
||||
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
||||
|
||||
/** READ-ONLY **/
|
||||
unsigned short http_major;
|
||||
unsigned short http_minor;
|
||||
unsigned int status_code : 16; /* responses only */
|
||||
unsigned int method : 8; /* requests only */
|
||||
unsigned int http_errno : 7;
|
||||
|
||||
/* 1 = Upgrade header was present and the parser has exited because of that.
|
||||
* 0 = No upgrade header present.
|
||||
* Should be checked when http_parser_execute() returns in addition to
|
||||
* error checking.
|
||||
*/
|
||||
unsigned int upgrade : 1;
|
||||
|
||||
/** PUBLIC **/
|
||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
||||
};
|
||||
|
||||
|
||||
struct http_parser_settings {
|
||||
http_cb on_message_begin;
|
||||
http_data_cb on_url;
|
||||
http_data_cb on_status;
|
||||
http_data_cb on_header_field;
|
||||
http_data_cb on_header_value;
|
||||
http_cb on_headers_complete;
|
||||
http_data_cb on_body;
|
||||
http_cb on_message_complete;
|
||||
/* When on_chunk_header is called, the current chunk length is stored
|
||||
* in parser->content_length.
|
||||
*/
|
||||
http_cb on_chunk_header;
|
||||
http_cb on_chunk_complete;
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_url_fields
|
||||
{ UF_SCHEMA = 0
|
||||
, UF_HOST = 1
|
||||
, UF_PORT = 2
|
||||
, UF_PATH = 3
|
||||
, UF_QUERY = 4
|
||||
, UF_FRAGMENT = 5
|
||||
, UF_USERINFO = 6
|
||||
, UF_MAX = 7
|
||||
};
|
||||
|
||||
|
||||
/* Result structure for http_parser_parse_url().
|
||||
*
|
||||
* Callers should index into field_data[] with UF_* values iff field_set
|
||||
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
||||
* because we probably have padding left over), we convert any port to
|
||||
* a uint16_t.
|
||||
*/
|
||||
struct http_parser_url {
|
||||
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
||||
uint16_t port; /* Converted UF_PORT string */
|
||||
|
||||
struct {
|
||||
uint16_t off; /* Offset into buffer in which field starts */
|
||||
uint16_t len; /* Length of run in buffer */
|
||||
} field_data[UF_MAX];
|
||||
};
|
||||
|
||||
|
||||
/* Returns the library version. Bits 16-23 contain the major version number,
|
||||
* bits 8-15 the minor version number and bits 0-7 the patch level.
|
||||
* Usage example:
|
||||
*
|
||||
* unsigned long version = http_parser_version();
|
||||
* unsigned major = (version >> 16) & 255;
|
||||
* unsigned minor = (version >> 8) & 255;
|
||||
* unsigned patch = version & 255;
|
||||
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
|
||||
*/
|
||||
unsigned long http_parser_version(void);
|
||||
|
||||
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
||||
|
||||
|
||||
/* Initialize http_parser_settings members to 0
|
||||
*/
|
||||
void http_parser_settings_init(http_parser_settings *settings);
|
||||
|
||||
|
||||
/* Executes the parser. Returns number of parsed bytes. Sets
|
||||
* `parser->http_errno` on error. */
|
||||
size_t http_parser_execute(http_parser *parser,
|
||||
const http_parser_settings *settings,
|
||||
const char *data,
|
||||
size_t len);
|
||||
|
||||
|
||||
/* If http_should_keep_alive() in the on_headers_complete or
|
||||
* on_message_complete callback returns 0, then this should be
|
||||
* the last message on the connection.
|
||||
* If you are the server, respond with the "Connection: close" header.
|
||||
* If you are the client, close the connection.
|
||||
*/
|
||||
int http_should_keep_alive(const http_parser *parser);
|
||||
|
||||
/* Returns a string version of the HTTP method. */
|
||||
const char *http_method_str(enum http_method m);
|
||||
|
||||
/* Return a string name of the given error */
|
||||
const char *http_errno_name(enum http_errno err);
|
||||
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err);
|
||||
|
||||
/* Initialize all http_parser_url members to 0 */
|
||||
void http_parser_url_init(struct http_parser_url *u);
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||
int is_connect,
|
||||
struct http_parser_url *u);
|
||||
|
||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
||||
void http_parser_pause(http_parser *parser, int paused);
|
||||
|
||||
/* Checks if this is the final chunk of the body. */
|
||||
int http_body_is_final(const http_parser *parser);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,815 @@
|
||||
#include "inspector_agent.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_io.h"
|
||||
#include "env.h"
|
||||
#include "node.h"
|
||||
#include "v8-inspector.h"
|
||||
#include "v8-platform.h"
|
||||
#include "util.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "libplatform/libplatform.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __POSIX__
|
||||
#include <unistd.h> // setuid, getuid
|
||||
#endif // __POSIX__
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
namespace {
|
||||
using v8::Context;
|
||||
using v8::External;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::HandleScope;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Maybe;
|
||||
using v8::MaybeLocal;
|
||||
using v8::NewStringType;
|
||||
using v8::Object;
|
||||
using v8::Persistent;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
using v8_inspector::StringBuffer;
|
||||
using v8_inspector::StringView;
|
||||
using v8_inspector::V8Inspector;
|
||||
using v8_inspector::V8InspectorClient;
|
||||
|
||||
static uv_sem_t start_io_thread_semaphore;
|
||||
static uv_async_t start_io_thread_async;
|
||||
|
||||
class StartIoTask : public v8::Task {
|
||||
public:
|
||||
explicit StartIoTask(Agent* agent) : agent(agent) {}
|
||||
|
||||
void Run() override {
|
||||
agent->StartIoThread(false);
|
||||
}
|
||||
|
||||
private:
|
||||
Agent* agent;
|
||||
};
|
||||
|
||||
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
|
||||
Local<Value> value) {
|
||||
TwoByteValue buffer(isolate, value);
|
||||
return StringBuffer::create(StringView(*buffer, buffer.length()));
|
||||
}
|
||||
|
||||
// Called on the main thread.
|
||||
void StartIoThreadAsyncCallback(uv_async_t* handle) {
|
||||
static_cast<Agent*>(handle->data)->StartIoThread(false);
|
||||
}
|
||||
|
||||
void StartIoInterrupt(Isolate* isolate, void* agent) {
|
||||
static_cast<Agent*>(agent)->StartIoThread(false);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __POSIX__
|
||||
|
||||
static void StartIoThreadWakeup(int signo) {
|
||||
uv_sem_post(&start_io_thread_semaphore);
|
||||
}
|
||||
|
||||
inline void* StartIoThreadMain(void* unused) {
|
||||
for (;;) {
|
||||
uv_sem_wait(&start_io_thread_semaphore);
|
||||
Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
|
||||
if (agent != nullptr)
|
||||
agent->RequestIoThreadStart();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int StartDebugSignalHandler() {
|
||||
// Start a watchdog thread for calling v8::Debug::DebugBreak() because
|
||||
// it's not safe to call directly from the signal handler, it can
|
||||
// deadlock with the thread it interrupts.
|
||||
CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
|
||||
pthread_attr_t attr;
|
||||
CHECK_EQ(0, pthread_attr_init(&attr));
|
||||
// Don't shrink the thread's stack on FreeBSD. Said platform decided to
|
||||
// follow the pthreads specification to the letter rather than in spirit:
|
||||
// https://lists.freebsd.org/pipermail/freebsd-current/2014-March/048885.html
|
||||
#ifndef __FreeBSD__
|
||||
CHECK_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN));
|
||||
#endif // __FreeBSD__
|
||||
CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
|
||||
sigset_t sigmask;
|
||||
// Mask all signals.
|
||||
sigfillset(&sigmask);
|
||||
CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
|
||||
pthread_t thread;
|
||||
const int err = pthread_create(&thread, &attr,
|
||||
StartIoThreadMain, nullptr);
|
||||
// Restore original mask
|
||||
CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
|
||||
CHECK_EQ(0, pthread_attr_destroy(&attr));
|
||||
if (err != 0) {
|
||||
SE_LOGE("node[%d]: pthread_create: %s\n", getpid(), strerror(err));
|
||||
|
||||
// Leave SIGUSR1 blocked. We don't install a signal handler,
|
||||
// receiving the signal would terminate the process.
|
||||
return -err;
|
||||
}
|
||||
RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
|
||||
// Unblock SIGUSR1. A pending SIGUSR1 signal will now be delivered.
|
||||
sigemptyset(&sigmask);
|
||||
sigaddset(&sigmask, SIGUSR1);
|
||||
CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
|
||||
return 0;
|
||||
}
|
||||
#endif // __POSIX__
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD WINAPI StartIoThreadProc(void* arg) {
|
||||
Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
|
||||
if (agent != nullptr)
|
||||
agent->RequestIoThreadStart();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
|
||||
size_t buf_len) {
|
||||
return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
|
||||
}
|
||||
|
||||
static int StartDebugSignalHandler() {
|
||||
wchar_t mapping_name[32];
|
||||
HANDLE mapping_handle;
|
||||
DWORD pid;
|
||||
LPTHREAD_START_ROUTINE* handler;
|
||||
|
||||
pid = GetCurrentProcessId();
|
||||
|
||||
if (GetDebugSignalHandlerMappingName(pid,
|
||||
mapping_name,
|
||||
arraysize(mapping_name)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
|
||||
nullptr,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
sizeof *handler,
|
||||
mapping_name);
|
||||
if (mapping_handle == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
|
||||
MapViewOfFile(mapping_handle,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0,
|
||||
0,
|
||||
sizeof *handler));
|
||||
if (handler == nullptr) {
|
||||
CloseHandle(mapping_handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*handler = StartIoThreadProc;
|
||||
|
||||
UnmapViewOfFile(static_cast<void*>(handler));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
class JsBindingsSessionDelegate : public InspectorSessionDelegate {
|
||||
public:
|
||||
JsBindingsSessionDelegate(Environment* env,
|
||||
Local<Object> session,
|
||||
Local<Object> receiver,
|
||||
Local<Function> callback)
|
||||
: env_(env),
|
||||
session_(env->isolate(), session),
|
||||
receiver_(env->isolate(), receiver),
|
||||
callback_(env->isolate(), callback) {
|
||||
session_.SetWeak(this, JsBindingsSessionDelegate::Release,
|
||||
v8::WeakCallbackType::kParameter);
|
||||
}
|
||||
|
||||
~JsBindingsSessionDelegate() override {
|
||||
session_.Reset();
|
||||
receiver_.Reset();
|
||||
callback_.Reset();
|
||||
}
|
||||
|
||||
bool WaitForFrontendMessageWhilePaused() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SendMessageToFrontend(const v8_inspector::StringView& message) override {
|
||||
Isolate* isolate = env_->isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
Context::Scope context_scope(env_->context());
|
||||
MaybeLocal<String> v8string =
|
||||
String::NewFromTwoByte(isolate, message.characters16(),
|
||||
NewStringType::kNormal, message.length());
|
||||
Local<Value> argument = v8string.ToLocalChecked().As<Value>();
|
||||
Local<Function> callback = callback_.Get(isolate);
|
||||
Local<Object> receiver = receiver_.Get(isolate);
|
||||
callback->Call(env_->context(), receiver, 1, &argument)
|
||||
.FromMaybe(Local<Value>());
|
||||
}
|
||||
|
||||
void Disconnect() {
|
||||
Agent* agent = env_->inspector_agent();
|
||||
if (agent->delegate() == this)
|
||||
agent->Disconnect();
|
||||
}
|
||||
|
||||
private:
|
||||
static void Release(
|
||||
const v8::WeakCallbackInfo<JsBindingsSessionDelegate>& info) {
|
||||
info.SetSecondPassCallback(ReleaseSecondPass);
|
||||
info.GetParameter()->session_.Reset();
|
||||
}
|
||||
|
||||
static void ReleaseSecondPass(
|
||||
const v8::WeakCallbackInfo<JsBindingsSessionDelegate>& info) {
|
||||
JsBindingsSessionDelegate* delegate = info.GetParameter();
|
||||
delegate->Disconnect();
|
||||
delete delegate;
|
||||
}
|
||||
|
||||
Environment* env_;
|
||||
Persistent<Object> session_;
|
||||
Persistent<Object> receiver_;
|
||||
Persistent<Function> callback_;
|
||||
};
|
||||
|
||||
void SetDelegate(Environment* env, Local<Object> inspector,
|
||||
JsBindingsSessionDelegate* delegate) {
|
||||
inspector->SetPrivate(env->context(),
|
||||
env->inspector_delegate_private_symbol(),
|
||||
v8::External::New(env->isolate(), delegate));
|
||||
}
|
||||
|
||||
Maybe<JsBindingsSessionDelegate*> GetDelegate(
|
||||
const FunctionCallbackInfo<Value>& info) {
|
||||
Environment* env = Environment::GetCurrent(info);
|
||||
Local<Value> delegate;
|
||||
MaybeLocal<Value> maybe_delegate =
|
||||
info.This()->GetPrivate(env->context(),
|
||||
env->inspector_delegate_private_symbol());
|
||||
|
||||
if (maybe_delegate.ToLocal(&delegate)) {
|
||||
CHECK(delegate->IsExternal());
|
||||
void* value = delegate.As<External>()->Value();
|
||||
if (value != nullptr) {
|
||||
return v8::Just(static_cast<JsBindingsSessionDelegate*>(value));
|
||||
}
|
||||
}
|
||||
env->ThrowError("Inspector is not connected");
|
||||
return v8::Nothing<JsBindingsSessionDelegate*>();
|
||||
}
|
||||
|
||||
void Dispatch(const FunctionCallbackInfo<Value>& info) {
|
||||
Environment* env = Environment::GetCurrent(info);
|
||||
if (!info[0]->IsString()) {
|
||||
env->ThrowError("Inspector message must be a string");
|
||||
return;
|
||||
}
|
||||
Maybe<JsBindingsSessionDelegate*> maybe_delegate = GetDelegate(info);
|
||||
if (maybe_delegate.IsNothing())
|
||||
return;
|
||||
Agent* inspector = env->inspector_agent();
|
||||
CHECK_EQ(maybe_delegate.ToChecked(), inspector->delegate());
|
||||
inspector->Dispatch(ToProtocolString(env->isolate(), info[0])->string());
|
||||
}
|
||||
|
||||
void Disconnect(const FunctionCallbackInfo<Value>& info) {
|
||||
Environment* env = Environment::GetCurrent(info);
|
||||
Maybe<JsBindingsSessionDelegate*> delegate = GetDelegate(info);
|
||||
if (delegate.IsNothing()) {
|
||||
return;
|
||||
}
|
||||
delegate.ToChecked()->Disconnect();
|
||||
SetDelegate(env, info.This(), nullptr);
|
||||
delete delegate.ToChecked();
|
||||
}
|
||||
|
||||
void ConnectJSBindingsSession(const FunctionCallbackInfo<Value>& info) {
|
||||
Environment* env = Environment::GetCurrent(info);
|
||||
if (!info[0]->IsFunction()) {
|
||||
env->ThrowError("Message callback is required");
|
||||
return;
|
||||
}
|
||||
Agent* inspector = env->inspector_agent();
|
||||
if (inspector->delegate() != nullptr) {
|
||||
env->ThrowError("Session is already attached");
|
||||
return;
|
||||
}
|
||||
Local<Object> session = Object::New(env->isolate());
|
||||
env->SetMethod(session, "dispatch", Dispatch);
|
||||
env->SetMethod(session, "disconnect", Disconnect);
|
||||
info.GetReturnValue().Set(session);
|
||||
|
||||
JsBindingsSessionDelegate* delegate =
|
||||
new JsBindingsSessionDelegate(env, session, info.Holder(),
|
||||
info[0].As<Function>());
|
||||
inspector->Connect(delegate);
|
||||
SetDelegate(env, session, delegate);
|
||||
}
|
||||
|
||||
void InspectorConsoleCall(const v8::FunctionCallbackInfo<Value>& info) {
|
||||
Isolate* isolate = info.GetIsolate();
|
||||
HandleScope handle_scope(isolate);
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
CHECK_LT(2, info.Length());
|
||||
std::vector<Local<Value>> call_args;
|
||||
for (int i = 3; i < info.Length(); ++i) {
|
||||
call_args.push_back(info[i]);
|
||||
}
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
if (env->inspector_agent()->enabled()) {
|
||||
Local<Value> inspector_method = info[0];
|
||||
CHECK(inspector_method->IsFunction());
|
||||
Local<Value> config_value = info[2];
|
||||
CHECK(config_value->IsObject());
|
||||
Local<Object> config_object = config_value.As<Object>();
|
||||
Local<String> in_call_key = FIXED_ONE_BYTE_STRING(isolate, "in_call");
|
||||
if (!config_object->Has(context, in_call_key).FromMaybe(false)) {
|
||||
CHECK(config_object->Set(context,
|
||||
in_call_key,
|
||||
v8::True(isolate)).FromJust());
|
||||
CHECK(!inspector_method.As<Function>()->Call(context,
|
||||
info.Holder(),
|
||||
call_args.size(),
|
||||
call_args.data()).IsEmpty());
|
||||
}
|
||||
CHECK(config_object->Delete(context, in_call_key).FromJust());
|
||||
}
|
||||
|
||||
Local<Value> node_method = info[1];
|
||||
CHECK(node_method->IsFunction());
|
||||
node_method.As<Function>()->Call(context,
|
||||
info.Holder(),
|
||||
call_args.size(),
|
||||
call_args.data()).FromMaybe(Local<Value>());
|
||||
}
|
||||
|
||||
void CallAndPauseOnStart(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK_GT(args.Length(), 1);
|
||||
CHECK(args[0]->IsFunction());
|
||||
std::vector<v8::Local<v8::Value>> call_args;
|
||||
for (int i = 2; i < args.Length(); i++) {
|
||||
call_args.push_back(args[i]);
|
||||
}
|
||||
|
||||
env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
|
||||
v8::MaybeLocal<v8::Value> retval =
|
||||
args[0].As<v8::Function>()->Call(env->context(), args[1],
|
||||
call_args.size(), call_args.data());
|
||||
if (!retval.IsEmpty()) {
|
||||
args.GetReturnValue().Set(retval.ToLocalChecked());
|
||||
}
|
||||
}
|
||||
|
||||
// Used in NodeInspectorClient::currentTimeMS() below.
|
||||
const int NANOS_PER_MSEC = 1000000;
|
||||
const int CONTEXT_GROUP_ID = 1;
|
||||
|
||||
class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
|
||||
public:
|
||||
explicit ChannelImpl(V8Inspector* inspector,
|
||||
InspectorSessionDelegate* delegate)
|
||||
: delegate_(delegate) {
|
||||
session_ = inspector->connect(1, this, StringView());
|
||||
}
|
||||
|
||||
virtual ~ChannelImpl() {}
|
||||
|
||||
void dispatchProtocolMessage(const StringView& message) {
|
||||
session_->dispatchProtocolMessage(message);
|
||||
}
|
||||
|
||||
bool waitForFrontendMessage() {
|
||||
return delegate_->WaitForFrontendMessageWhilePaused();
|
||||
}
|
||||
|
||||
void schedulePauseOnNextStatement(const std::string& reason) {
|
||||
std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
|
||||
session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
|
||||
}
|
||||
|
||||
InspectorSessionDelegate* delegate() {
|
||||
return delegate_;
|
||||
}
|
||||
|
||||
private:
|
||||
void sendResponse(
|
||||
int callId,
|
||||
std::unique_ptr<v8_inspector::StringBuffer> message) override {
|
||||
sendMessageToFrontend(message->string());
|
||||
}
|
||||
|
||||
void sendNotification(
|
||||
std::unique_ptr<v8_inspector::StringBuffer> message) override {
|
||||
sendMessageToFrontend(message->string());
|
||||
}
|
||||
|
||||
void flushProtocolNotifications() override { }
|
||||
|
||||
void sendMessageToFrontend(const StringView& message) {
|
||||
delegate_->SendMessageToFrontend(message);
|
||||
}
|
||||
|
||||
InspectorSessionDelegate* const delegate_;
|
||||
std::unique_ptr<v8_inspector::V8InspectorSession> session_;
|
||||
};
|
||||
|
||||
class InspectorTimer {
|
||||
public:
|
||||
InspectorTimer(uv_loop_t* loop,
|
||||
double interval_s,
|
||||
V8InspectorClient::TimerCallback callback,
|
||||
void* data) : timer_(),
|
||||
callback_(callback),
|
||||
data_(data) {
|
||||
uv_timer_init(loop, &timer_);
|
||||
int64_t interval_ms = 1000 * interval_s;
|
||||
uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms);
|
||||
}
|
||||
|
||||
InspectorTimer(const InspectorTimer&) = delete;
|
||||
|
||||
void Stop() {
|
||||
uv_timer_stop(&timer_);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&timer_), TimerClosedCb);
|
||||
}
|
||||
|
||||
private:
|
||||
static void OnTimer(uv_timer_t* uvtimer) {
|
||||
InspectorTimer* timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer);
|
||||
timer->callback_(timer->data_);
|
||||
}
|
||||
|
||||
static void TimerClosedCb(uv_handle_t* uvtimer) {
|
||||
InspectorTimer* timer =
|
||||
node::ContainerOf(&InspectorTimer::timer_,
|
||||
reinterpret_cast<uv_timer_t*>(uvtimer));
|
||||
delete timer;
|
||||
}
|
||||
|
||||
~InspectorTimer() {}
|
||||
|
||||
uv_timer_t timer_;
|
||||
V8InspectorClient::TimerCallback callback_;
|
||||
void* data_;
|
||||
};
|
||||
|
||||
class InspectorTimerHandle {
|
||||
public:
|
||||
InspectorTimerHandle(uv_loop_t* loop, double interval_s,
|
||||
V8InspectorClient::TimerCallback callback, void* data) {
|
||||
timer_ = new InspectorTimer(loop, interval_s, callback, data);
|
||||
}
|
||||
|
||||
InspectorTimerHandle(const InspectorTimerHandle&) = delete;
|
||||
|
||||
~InspectorTimerHandle() {
|
||||
CHECK_NE(timer_, nullptr);
|
||||
timer_->Stop();
|
||||
timer_ = nullptr;
|
||||
}
|
||||
private:
|
||||
InspectorTimer* timer_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class NodeInspectorClient : public V8InspectorClient {
|
||||
public:
|
||||
NodeInspectorClient(node::Environment* env,
|
||||
v8::Platform* platform) : env_(env),
|
||||
platform_(platform),
|
||||
terminated_(false),
|
||||
running_nested_loop_(false) {
|
||||
client_ = V8Inspector::create(env->isolate(), this);
|
||||
}
|
||||
|
||||
void runMessageLoopOnPause(int context_group_id) override {
|
||||
CHECK_NE(channel_, nullptr);
|
||||
if (running_nested_loop_)
|
||||
return;
|
||||
terminated_ = false;
|
||||
running_nested_loop_ = true;
|
||||
while (!terminated_ && channel_->waitForFrontendMessage()) {
|
||||
while (v8::platform::PumpMessageLoop(platform_, env_->isolate()))
|
||||
{}
|
||||
}
|
||||
terminated_ = false;
|
||||
running_nested_loop_ = false;
|
||||
}
|
||||
|
||||
double currentTimeMS() override {
|
||||
return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
|
||||
}
|
||||
|
||||
void contextCreated(Local<Context> context, const std::string& name) {
|
||||
std::unique_ptr<StringBuffer> name_buffer = Utf8ToStringView(name);
|
||||
v8_inspector::V8ContextInfo info(context, CONTEXT_GROUP_ID,
|
||||
name_buffer->string());
|
||||
client_->contextCreated(info);
|
||||
}
|
||||
|
||||
void contextDestroyed(Local<Context> context) {
|
||||
client_->contextDestroyed(context);
|
||||
}
|
||||
|
||||
void quitMessageLoopOnPause() override {
|
||||
terminated_ = true;
|
||||
}
|
||||
|
||||
void connectFrontend(InspectorSessionDelegate* delegate) {
|
||||
CHECK_EQ(channel_, nullptr);
|
||||
channel_ = std::unique_ptr<ChannelImpl>(
|
||||
new ChannelImpl(client_.get(), delegate));
|
||||
}
|
||||
|
||||
void disconnectFrontend() {
|
||||
quitMessageLoopOnPause();
|
||||
channel_.reset();
|
||||
}
|
||||
|
||||
void dispatchMessageFromFrontend(const StringView& message) {
|
||||
CHECK_NE(channel_, nullptr);
|
||||
channel_->dispatchProtocolMessage(message);
|
||||
}
|
||||
|
||||
Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
|
||||
return env_->context();
|
||||
}
|
||||
|
||||
void FatalException(Local<Value> error, Local<v8::Message> message) {
|
||||
Local<Context> context = env_->context();
|
||||
|
||||
int script_id = message->GetScriptOrigin().ScriptID()->Value();
|
||||
|
||||
Local<v8::StackTrace> stack_trace = message->GetStackTrace();
|
||||
|
||||
if (!stack_trace.IsEmpty() &&
|
||||
stack_trace->GetFrameCount() > 0 &&
|
||||
script_id == stack_trace->GetFrame(env_->isolate(), 0)->GetScriptId()) {
|
||||
script_id = 0;
|
||||
}
|
||||
|
||||
const uint8_t DETAILS[] = "Uncaught";
|
||||
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
|
||||
client_->exceptionThrown(
|
||||
context,
|
||||
StringView(DETAILS, sizeof(DETAILS) - 1),
|
||||
error,
|
||||
ToProtocolString(isolate, message->Get())->string(),
|
||||
ToProtocolString(isolate, message->GetScriptResourceName())->string(),
|
||||
message->GetLineNumber(context).FromMaybe(0),
|
||||
message->GetStartColumn(context).FromMaybe(0),
|
||||
client_->createStackTrace(stack_trace),
|
||||
script_id);
|
||||
}
|
||||
|
||||
ChannelImpl* channel() {
|
||||
return channel_.get();
|
||||
}
|
||||
|
||||
void startRepeatingTimer(double interval_s,
|
||||
TimerCallback callback,
|
||||
void* data) override {
|
||||
timers_.emplace(std::piecewise_construct, std::make_tuple(data),
|
||||
std::make_tuple(env_->event_loop(), interval_s, callback,
|
||||
data));
|
||||
}
|
||||
|
||||
void cancelTimer(void* data) override {
|
||||
timers_.erase(data);
|
||||
}
|
||||
|
||||
private:
|
||||
node::Environment* env_;
|
||||
v8::Platform* platform_;
|
||||
bool terminated_;
|
||||
bool running_nested_loop_;
|
||||
std::unique_ptr<V8Inspector> client_;
|
||||
std::unique_ptr<ChannelImpl> channel_;
|
||||
std::unordered_map<void*, InspectorTimerHandle> timers_;
|
||||
};
|
||||
|
||||
Agent::Agent(Environment* env) : parent_env_(env),
|
||||
client_(nullptr),
|
||||
platform_(nullptr),
|
||||
enabled_(false) {}
|
||||
|
||||
// Destructor needs to be defined here in implementation file as the header
|
||||
// does not have full definition of some classes.
|
||||
Agent::~Agent() {
|
||||
}
|
||||
|
||||
bool Agent::Start(v8::Platform* platform, const char* path,
|
||||
const DebugOptions& options) {
|
||||
path_ = path == nullptr ? "" : path;
|
||||
debug_options_ = options;
|
||||
client_ =
|
||||
std::unique_ptr<NodeInspectorClient>(
|
||||
new NodeInspectorClient(parent_env_, platform));
|
||||
client_->contextCreated(parent_env_->context(), "Node.js Main Context");
|
||||
platform_ = platform;
|
||||
CHECK_EQ(0, uv_async_init(uv_default_loop(),
|
||||
&start_io_thread_async,
|
||||
StartIoThreadAsyncCallback));
|
||||
start_io_thread_async.data = this;
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
|
||||
|
||||
// Ignore failure, SIGUSR1 won't work, but that should not block node start.
|
||||
StartDebugSignalHandler();
|
||||
if (options.inspector_enabled()) {
|
||||
// This will return false if listen failed on the inspector port.
|
||||
return StartIoThread(options.wait_for_connect());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Agent::StartIoThread(bool wait_for_connect) {
|
||||
if (io_ != nullptr)
|
||||
return true;
|
||||
|
||||
CHECK_NE(client_, nullptr);
|
||||
|
||||
enabled_ = true;
|
||||
io_ = std::unique_ptr<InspectorIo>(
|
||||
new InspectorIo(parent_env_, platform_, path_, debug_options_,
|
||||
wait_for_connect));
|
||||
if (!io_->Start()) {
|
||||
client_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
v8::Isolate* isolate = parent_env_->isolate();
|
||||
|
||||
// Send message to enable debug in workers
|
||||
HandleScope handle_scope(isolate);
|
||||
Local<Object> process_object = parent_env_->process_object();
|
||||
Local<Value> emit_fn =
|
||||
process_object->Get(isolate->GetCurrentContext(), FIXED_ONE_BYTE_STRING(isolate, "emit")).ToLocalChecked();
|
||||
// In case the thread started early during the startup
|
||||
if (!emit_fn->IsFunction())
|
||||
return true;
|
||||
|
||||
Local<Object> message = Object::New(isolate);
|
||||
message->Set(parent_env_->context(), FIXED_ONE_BYTE_STRING(isolate, "cmd"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED"));
|
||||
Local<Value> argv[] = {
|
||||
FIXED_ONE_BYTE_STRING(isolate, "internalMessage"),
|
||||
message
|
||||
};
|
||||
MakeCallback(parent_env_->isolate(), process_object, emit_fn.As<Function>(),
|
||||
arraysize(argv), argv, {0, 0});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Agent::Stop() {
|
||||
if (io_ != nullptr) {
|
||||
io_->Stop();
|
||||
io_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::Connect(InspectorSessionDelegate* delegate) {
|
||||
enabled_ = true;
|
||||
client_->connectFrontend(delegate);
|
||||
}
|
||||
|
||||
bool Agent::IsConnected() {
|
||||
return io_ && io_->IsConnected();
|
||||
}
|
||||
|
||||
void Agent::WaitForDisconnect() {
|
||||
CHECK_NE(client_, nullptr);
|
||||
client_->contextDestroyed(parent_env_->context());
|
||||
if (io_ != nullptr) {
|
||||
io_->WaitForDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::FatalException(Local<Value> error, Local<v8::Message> message) {
|
||||
if (!IsStarted())
|
||||
return;
|
||||
client_->FatalException(error, message);
|
||||
WaitForDisconnect();
|
||||
}
|
||||
|
||||
void Agent::Dispatch(const StringView& message) {
|
||||
CHECK_NE(client_, nullptr);
|
||||
client_->dispatchMessageFromFrontend(message);
|
||||
}
|
||||
|
||||
void Agent::Disconnect() {
|
||||
CHECK_NE(client_, nullptr);
|
||||
client_->disconnectFrontend();
|
||||
}
|
||||
|
||||
void Agent::RunMessageLoop() {
|
||||
CHECK_NE(client_, nullptr);
|
||||
client_->runMessageLoopOnPause(CONTEXT_GROUP_ID);
|
||||
}
|
||||
|
||||
InspectorSessionDelegate* Agent::delegate() {
|
||||
CHECK_NE(client_, nullptr);
|
||||
ChannelImpl* channel = client_->channel();
|
||||
if (channel == nullptr)
|
||||
return nullptr;
|
||||
return channel->delegate();
|
||||
}
|
||||
|
||||
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
|
||||
ChannelImpl* channel = client_->channel();
|
||||
if (channel != nullptr)
|
||||
channel->schedulePauseOnNextStatement(reason);
|
||||
}
|
||||
|
||||
void Open(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
inspector::Agent* agent = env->inspector_agent();
|
||||
bool wait_for_connect = false;
|
||||
|
||||
if (args.Length() > 0 && args[0]->IsUint32()) {
|
||||
uint32_t port = args[0]->Uint32Value(env->context()).ToChecked();
|
||||
agent->options().set_port(static_cast<int>(port));
|
||||
}
|
||||
|
||||
if (args.Length() > 1 && args[1]->IsString()) {
|
||||
node::Utf8Value host(env->isolate(), args[1].As<String>());
|
||||
agent->options().set_host_name(*host);
|
||||
}
|
||||
|
||||
if (args.Length() > 2 && args[2]->IsBoolean()) {
|
||||
wait_for_connect = args[2]->BooleanValue(env->isolate());
|
||||
}
|
||||
|
||||
agent->StartIoThread(wait_for_connect);
|
||||
}
|
||||
|
||||
void Url(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
inspector::Agent* agent = env->inspector_agent();
|
||||
inspector::InspectorIo* io = agent->io();
|
||||
|
||||
if (!io) return;
|
||||
|
||||
std::vector<std::string> ids = io->GetTargetIds();
|
||||
|
||||
if (ids.empty()) return;
|
||||
|
||||
std::string url = FormatWsAddress(io->host(), io->port(), ids[0], true);
|
||||
args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str()));
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void Agent::InitInspector(Local<Object> target, Local<Value> unused,
|
||||
Local<Context> context, void* priv) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
Agent* agent = env->inspector_agent();
|
||||
env->SetMethod(target, "consoleCall", InspectorConsoleCall);
|
||||
if (agent->debug_options_.wait_for_connect())
|
||||
env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart);
|
||||
env->SetMethod(target, "connect", ConnectJSBindingsSession);
|
||||
env->SetMethod(target, "open", Open);
|
||||
env->SetMethod(target, "url", Url);
|
||||
}
|
||||
|
||||
void Agent::RequestIoThreadStart() {
|
||||
// We need to attempt to interrupt V8 flow (in case Node is running
|
||||
// continuous JS code) and to wake up libuv thread (in case Node is waiting
|
||||
// for IO events)
|
||||
uv_async_send(&start_io_thread_async);
|
||||
v8::Isolate* isolate = parent_env_->isolate();
|
||||
platform_->CallOnForegroundThread(isolate, new StartIoTask(this));
|
||||
isolate->RequestInterrupt(StartIoInterrupt, this);
|
||||
uv_async_send(&start_io_thread_async);
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
//cjh NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector,
|
||||
// node::inspector::Agent::InitInspector);
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef SRC_INSPECTOR_AGENT_H_
|
||||
#define SRC_INSPECTOR_AGENT_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if !HAVE_INSPECTOR
|
||||
#error("This header can only be used when inspector is enabled")
|
||||
#endif
|
||||
|
||||
#include "node_debug_options.h"
|
||||
|
||||
// Forward declaration to break recursive dependency chain with src/env.h.
|
||||
namespace node {
|
||||
class Environment;
|
||||
} // namespace node
|
||||
|
||||
namespace v8 {
|
||||
class Context;
|
||||
template <typename V>
|
||||
class FunctionCallbackInfo;
|
||||
template<typename T>
|
||||
class Local;
|
||||
class Message;
|
||||
class Object;
|
||||
class Platform;
|
||||
class Value;
|
||||
} // namespace v8
|
||||
|
||||
namespace v8_inspector {
|
||||
class StringView;
|
||||
} // namespace v8_inspector
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
class InspectorSessionDelegate {
|
||||
public:
|
||||
virtual ~InspectorSessionDelegate() = default;
|
||||
virtual bool WaitForFrontendMessageWhilePaused() = 0;
|
||||
virtual void SendMessageToFrontend(const v8_inspector::StringView& message)
|
||||
= 0;
|
||||
};
|
||||
|
||||
class InspectorIo;
|
||||
class NodeInspectorClient;
|
||||
|
||||
class Agent {
|
||||
public:
|
||||
explicit Agent(node::Environment* env);
|
||||
~Agent();
|
||||
|
||||
// Create client_, may create io_ if option enabled
|
||||
bool Start(v8::Platform* platform, const char* path,
|
||||
const DebugOptions& options);
|
||||
// Stop and destroy io_
|
||||
void Stop();
|
||||
|
||||
bool IsStarted() { return !!client_; }
|
||||
|
||||
// IO thread started, and client connected
|
||||
bool IsConnected();
|
||||
|
||||
|
||||
void WaitForDisconnect();
|
||||
void FatalException(v8::Local<v8::Value> error,
|
||||
v8::Local<v8::Message> message);
|
||||
|
||||
// These methods are called by the WS protocol and JS binding to create
|
||||
// inspector sessions. The inspector responds by using the delegate to send
|
||||
// messages back.
|
||||
void Connect(InspectorSessionDelegate* delegate);
|
||||
void Disconnect();
|
||||
void Dispatch(const v8_inspector::StringView& message);
|
||||
InspectorSessionDelegate* delegate();
|
||||
|
||||
void RunMessageLoop();
|
||||
bool enabled() { return enabled_; }
|
||||
void PauseOnNextJavascriptStatement(const std::string& reason);
|
||||
|
||||
// Initialize 'inspector' module bindings
|
||||
static void InitInspector(v8::Local<v8::Object> target,
|
||||
v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context,
|
||||
void* priv);
|
||||
|
||||
InspectorIo* io() {
|
||||
return io_.get();
|
||||
}
|
||||
|
||||
// Can only be called from the the main thread.
|
||||
bool StartIoThread(bool wait_for_connect);
|
||||
|
||||
// Calls StartIoThread() from off the main thread.
|
||||
void RequestIoThreadStart();
|
||||
|
||||
DebugOptions& options() { return debug_options_; }
|
||||
|
||||
private:
|
||||
node::Environment* parent_env_;
|
||||
std::unique_ptr<NodeInspectorClient> client_;
|
||||
std::unique_ptr<InspectorIo> io_;
|
||||
v8::Platform* platform_;
|
||||
bool enabled_;
|
||||
std::string path_;
|
||||
DebugOptions debug_options_;
|
||||
};
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_INSPECTOR_AGENT_H_
|
||||
@@ -0,0 +1,535 @@
|
||||
#include "inspector_io.h"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_socket_server.h"
|
||||
#include "env.h"
|
||||
#include "node.h"
|
||||
//cjh #include "node_crypto.h"
|
||||
#include "node_mutex.h"
|
||||
#include "v8-inspector.h"
|
||||
#include "util.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "libplatform/libplatform.h"
|
||||
|
||||
#include <sstream>
|
||||
//cjh #include <unicode/unistr.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "base/ccUTF8.h" //cjh added
|
||||
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
namespace {
|
||||
using AsyncAndAgent = std::pair<uv_async_t, Agent*>;
|
||||
using v8_inspector::StringBuffer;
|
||||
using v8_inspector::StringView;
|
||||
|
||||
template<typename Transport>
|
||||
using TransportAndIo = std::pair<Transport*, InspectorIo*>;
|
||||
|
||||
std::string GetProcessTitle() {
|
||||
char title[2048];
|
||||
int err = uv_get_process_title(title, sizeof(title));
|
||||
if (err == 0) {
|
||||
return title;
|
||||
} else {
|
||||
// Title is too long, or could not be retrieved.
|
||||
return "Node.js";
|
||||
}
|
||||
}
|
||||
|
||||
std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
|
||||
std::string script_path;
|
||||
|
||||
if (!script_name.empty()) {
|
||||
uv_fs_t req;
|
||||
req.ptr = nullptr;
|
||||
if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
|
||||
CHECK_NE(req.ptr, nullptr);
|
||||
script_path = std::string(static_cast<char*>(req.ptr));
|
||||
}
|
||||
uv_fs_req_cleanup(&req);
|
||||
}
|
||||
|
||||
return script_path;
|
||||
}
|
||||
|
||||
// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
|
||||
// Used ver 4 - with numbers
|
||||
std::string GenerateID() {
|
||||
// uint16_t buffer[8];
|
||||
|
||||
//cjh same uuid
|
||||
uint16_t buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
//cjh
|
||||
//cjh CHECK(crypto::EntropySource(reinterpret_cast<unsigned char*>(buffer),
|
||||
// sizeof(buffer)));
|
||||
|
||||
char uuid[256];
|
||||
snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
|
||||
buffer[0], // time_low
|
||||
buffer[1], // time_mid
|
||||
buffer[2], // time_low
|
||||
(buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version
|
||||
(buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low
|
||||
buffer[5], // node
|
||||
buffer[6],
|
||||
buffer[7]);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::string StringViewToUtf8(const StringView& view) {
|
||||
if (view.is8Bit()) {
|
||||
return std::string(reinterpret_cast<const char*>(view.characters8()),
|
||||
view.length());
|
||||
}
|
||||
const uint16_t* source = view.characters16();
|
||||
|
||||
std::u16string u16Str((char16_t*)source);
|
||||
std::string ret;
|
||||
cocos2d::StringUtils::UTF16ToUTF8(u16Str, ret);
|
||||
return ret;
|
||||
// const UChar* unicodeSource = reinterpret_cast<const UChar*>(source);
|
||||
// static_assert(sizeof(*source) == sizeof(*unicodeSource),
|
||||
// "sizeof(*source) == sizeof(*unicodeSource)");
|
||||
//
|
||||
// size_t result_length = view.length() * sizeof(*source);
|
||||
// std::string result(result_length, '\0');
|
||||
//cjh UnicodeString utf16(unicodeSource, view.length());
|
||||
// // ICU components for std::string compatibility are not enabled in build...
|
||||
// bool done = false;
|
||||
// while (!done) {
|
||||
// CheckedArrayByteSink sink(&result[0], result_length);
|
||||
// utf16.toUTF8(sink);
|
||||
// result_length = sink.NumberOfBytesAppended();
|
||||
// result.resize(result_length);
|
||||
// done = !sink.Overflowed();
|
||||
// }
|
||||
// return result;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void HandleSyncCloseCb(uv_handle_t* handle) {
|
||||
*static_cast<bool*>(handle->data) = true;
|
||||
}
|
||||
|
||||
int CloseAsyncAndLoop(uv_async_t* async) {
|
||||
bool is_closed = false;
|
||||
async->data = &is_closed;
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(async), HandleSyncCloseCb);
|
||||
while (!is_closed)
|
||||
uv_run(async->loop, UV_RUN_ONCE);
|
||||
async->data = nullptr;
|
||||
return uv_loop_close(async->loop);
|
||||
}
|
||||
|
||||
// Delete main_thread_req_ on async handle close
|
||||
void ReleasePairOnAsyncClose(uv_handle_t* async) {
|
||||
AsyncAndAgent* pair = node::ContainerOf(&AsyncAndAgent::first,
|
||||
reinterpret_cast<uv_async_t*>(async));
|
||||
delete pair;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<StringBuffer> Utf8ToStringView(const std::string& message) {
|
||||
//cjh UnicodeString utf16 =
|
||||
// UnicodeString::fromUTF8(StringPiece(message.data(), message.length()));
|
||||
|
||||
std::u16string u16Str;
|
||||
cocos2d::StringUtils::UTF8ToUTF16(message, u16Str);
|
||||
|
||||
// StringView view(reinterpret_cast<const uint16_t*>(utf16.getBuffer()),
|
||||
// utf16.length());
|
||||
|
||||
StringView view(reinterpret_cast<const uint16_t*>(u16Str.c_str()),
|
||||
u16Str.length());
|
||||
return StringBuffer::create(view);
|
||||
}
|
||||
|
||||
|
||||
class IoSessionDelegate : public InspectorSessionDelegate {
|
||||
public:
|
||||
explicit IoSessionDelegate(InspectorIo* io) : io_(io) { }
|
||||
bool WaitForFrontendMessageWhilePaused() override;
|
||||
void SendMessageToFrontend(const v8_inspector::StringView& message) override;
|
||||
private:
|
||||
InspectorIo* io_;
|
||||
};
|
||||
|
||||
// Passed to InspectorSocketServer to handle WS inspector protocol events,
|
||||
// mostly session start, message received, and session end.
|
||||
class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
|
||||
public:
|
||||
InspectorIoDelegate(InspectorIo* io, const std::string& script_path,
|
||||
const std::string& script_name, bool wait);
|
||||
// Calls PostIncomingMessage() with appropriate InspectorAction:
|
||||
// kStartSession
|
||||
bool StartSession(int session_id, const std::string& target_id) override;
|
||||
// kSendMessage
|
||||
void MessageReceived(int session_id, const std::string& message) override;
|
||||
// kEndSession
|
||||
void EndSession(int session_id) override;
|
||||
|
||||
std::vector<std::string> GetTargetIds() override;
|
||||
std::string GetTargetTitle(const std::string& id) override;
|
||||
std::string GetTargetUrl(const std::string& id) override;
|
||||
bool IsConnected() { return connected_; }
|
||||
void ServerDone() override {
|
||||
io_->ServerDone();
|
||||
}
|
||||
|
||||
private:
|
||||
InspectorIo* io_;
|
||||
bool connected_;
|
||||
int session_id_;
|
||||
const std::string script_name_;
|
||||
const std::string script_path_;
|
||||
const std::string target_id_;
|
||||
bool waiting_;
|
||||
};
|
||||
|
||||
void InterruptCallback(v8::Isolate*, void* agent) {
|
||||
InspectorIo* io = static_cast<Agent*>(agent)->io();
|
||||
if (io != nullptr)
|
||||
io->DispatchMessages();
|
||||
}
|
||||
|
||||
class DispatchMessagesTask : public v8::Task {
|
||||
public:
|
||||
explicit DispatchMessagesTask(Agent* agent) : agent_(agent) {}
|
||||
|
||||
void Run() override {
|
||||
InspectorIo* io = agent_->io();
|
||||
if (io != nullptr)
|
||||
io->DispatchMessages();
|
||||
}
|
||||
|
||||
private:
|
||||
Agent* agent_;
|
||||
};
|
||||
|
||||
InspectorIo::InspectorIo(Environment* env, v8::Platform* platform,
|
||||
const std::string& path, const DebugOptions& options,
|
||||
bool wait_for_connect)
|
||||
: options_(options), thread_(), delegate_(nullptr),
|
||||
state_(State::kNew), parent_env_(env),
|
||||
thread_req_(), platform_(platform),
|
||||
dispatching_messages_(false), session_id_(0),
|
||||
script_name_(path),
|
||||
wait_for_connect_(wait_for_connect), port_(-1) {
|
||||
main_thread_req_ = new AsyncAndAgent({uv_async_t(), env->inspector_agent()});
|
||||
CHECK_EQ(0, uv_async_init(env->event_loop(), &main_thread_req_->first,
|
||||
InspectorIo::MainThreadReqAsyncCb));
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(&main_thread_req_->first));
|
||||
CHECK_EQ(0, uv_sem_init(&thread_start_sem_, 0));
|
||||
}
|
||||
|
||||
InspectorIo::~InspectorIo() {
|
||||
uv_sem_destroy(&thread_start_sem_);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&main_thread_req_->first),
|
||||
ReleasePairOnAsyncClose);
|
||||
if (main_thread_req_)
|
||||
{
|
||||
delete main_thread_req_;
|
||||
main_thread_req_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool InspectorIo::Start() {
|
||||
CHECK_EQ(state_, State::kNew);
|
||||
CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0);
|
||||
uv_sem_wait(&thread_start_sem_);
|
||||
|
||||
if (state_ == State::kError) {
|
||||
return false;
|
||||
}
|
||||
state_ = State::kAccepting;
|
||||
if (wait_for_connect_) {
|
||||
DispatchMessages();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InspectorIo::Stop() {
|
||||
CHECK(state_ == State::kAccepting || state_ == State::kConnected);
|
||||
Write(TransportAction::kKill, 0, StringView());
|
||||
int err = uv_thread_join(&thread_);
|
||||
CHECK_EQ(err, 0);
|
||||
state_ = State::kShutDown;
|
||||
DispatchMessages();
|
||||
}
|
||||
|
||||
bool InspectorIo::IsConnected() {
|
||||
return delegate_ != nullptr && delegate_->IsConnected();
|
||||
}
|
||||
|
||||
bool InspectorIo::IsStarted() {
|
||||
return platform_ != nullptr;
|
||||
}
|
||||
|
||||
void InspectorIo::WaitForDisconnect() {
|
||||
if (state_ == State::kAccepting)
|
||||
state_ = State::kDone;
|
||||
if (state_ == State::kConnected) {
|
||||
state_ = State::kShutDown;
|
||||
Write(TransportAction::kStop, 0, StringView());
|
||||
SE_LOGD("Waiting for the debugger to disconnect...\n");
|
||||
parent_env_->inspector_agent()->RunMessageLoop();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void InspectorIo::ThreadMain(void* io) {
|
||||
static_cast<InspectorIo*>(io)->ThreadMain<InspectorSocketServer>();
|
||||
}
|
||||
|
||||
// static
|
||||
template <typename Transport>
|
||||
void InspectorIo::IoThreadAsyncCb(uv_async_t* async) {
|
||||
TransportAndIo<Transport>* transport_and_io =
|
||||
static_cast<TransportAndIo<Transport>*>(async->data);
|
||||
if (transport_and_io == nullptr) {
|
||||
return;
|
||||
}
|
||||
Transport* transport = transport_and_io->first;
|
||||
InspectorIo* io = transport_and_io->second;
|
||||
MessageQueue<TransportAction> outgoing_message_queue;
|
||||
io->SwapBehindLock(&io->outgoing_message_queue_, &outgoing_message_queue);
|
||||
for (const auto& outgoing : outgoing_message_queue) {
|
||||
switch (std::get<0>(outgoing)) {
|
||||
case TransportAction::kKill:
|
||||
transport->TerminateConnections();
|
||||
// Fallthrough
|
||||
case TransportAction::kStop:
|
||||
transport->Stop(nullptr);
|
||||
break;
|
||||
case TransportAction::kSendMessage:
|
||||
std::string message = StringViewToUtf8(std::get<2>(outgoing)->string());
|
||||
transport->Send(std::get<1>(outgoing), message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Transport>
|
||||
void InspectorIo::ThreadMain() {
|
||||
uv_loop_t loop;
|
||||
loop.data = nullptr;
|
||||
int err = uv_loop_init(&loop);
|
||||
CHECK_EQ(err, 0);
|
||||
thread_req_.data = nullptr;
|
||||
err = uv_async_init(&loop, &thread_req_, IoThreadAsyncCb<Transport>);
|
||||
CHECK_EQ(err, 0);
|
||||
std::string script_path = ScriptPath(&loop, script_name_);
|
||||
InspectorIoDelegate delegate(this, script_path, script_name_,
|
||||
wait_for_connect_);
|
||||
delegate_ = &delegate;
|
||||
Transport server(&delegate, &loop, options_.host_name(), options_.port());
|
||||
TransportAndIo<Transport> queue_transport(&server, this);
|
||||
thread_req_.data = &queue_transport;
|
||||
if (!server.Start()) {
|
||||
state_ = State::kError; // Safe, main thread is waiting on semaphore
|
||||
CHECK_EQ(0, CloseAsyncAndLoop(&thread_req_));
|
||||
uv_sem_post(&thread_start_sem_);
|
||||
return;
|
||||
}
|
||||
port_ = server.Port(); // Safe, main thread is waiting on semaphore.
|
||||
if (!wait_for_connect_) {
|
||||
uv_sem_post(&thread_start_sem_);
|
||||
}
|
||||
uv_run(&loop, UV_RUN_DEFAULT);
|
||||
thread_req_.data = nullptr;
|
||||
CHECK_EQ(uv_loop_close(&loop), 0);
|
||||
delegate_ = nullptr;
|
||||
}
|
||||
|
||||
template <typename ActionType>
|
||||
bool InspectorIo::AppendMessage(MessageQueue<ActionType>* queue,
|
||||
ActionType action, int session_id,
|
||||
std::unique_ptr<StringBuffer> buffer) {
|
||||
Mutex::ScopedLock scoped_lock(state_lock_);
|
||||
bool trigger_pumping = queue->empty();
|
||||
queue->push_back(std::make_tuple(action, session_id, std::move(buffer)));
|
||||
return trigger_pumping;
|
||||
}
|
||||
|
||||
template <typename ActionType>
|
||||
void InspectorIo::SwapBehindLock(MessageQueue<ActionType>* vector1,
|
||||
MessageQueue<ActionType>* vector2) {
|
||||
Mutex::ScopedLock scoped_lock(state_lock_);
|
||||
vector1->swap(*vector2);
|
||||
}
|
||||
|
||||
void InspectorIo::PostIncomingMessage(InspectorAction action, int session_id,
|
||||
const std::string& message) {
|
||||
if (AppendMessage(&incoming_message_queue_, action, session_id,
|
||||
Utf8ToStringView(message))) {
|
||||
Agent* agent = main_thread_req_->second;
|
||||
v8::Isolate* isolate = parent_env_->isolate();
|
||||
platform_->CallOnForegroundThread(isolate,
|
||||
new DispatchMessagesTask(agent));
|
||||
isolate->RequestInterrupt(InterruptCallback, agent);
|
||||
CHECK_EQ(0, uv_async_send(&main_thread_req_->first));
|
||||
}
|
||||
NotifyMessageReceived();
|
||||
}
|
||||
|
||||
std::vector<std::string> InspectorIo::GetTargetIds() const {
|
||||
return delegate_ ? delegate_->GetTargetIds() : std::vector<std::string>();
|
||||
}
|
||||
|
||||
void InspectorIo::WaitForFrontendMessageWhilePaused() {
|
||||
dispatching_messages_ = false;
|
||||
Mutex::ScopedLock scoped_lock(state_lock_);
|
||||
if (incoming_message_queue_.empty())
|
||||
incoming_message_cond_.Wait(scoped_lock);
|
||||
}
|
||||
|
||||
void InspectorIo::NotifyMessageReceived() {
|
||||
Mutex::ScopedLock scoped_lock(state_lock_);
|
||||
incoming_message_cond_.Broadcast(scoped_lock);
|
||||
}
|
||||
|
||||
void InspectorIo::DispatchMessages() {
|
||||
// This function can be reentered if there was an incoming message while
|
||||
// V8 was processing another inspector request (e.g. if the user is
|
||||
// evaluating a long-running JS code snippet). This can happen only at
|
||||
// specific points (e.g. the lines that call inspector_ methods)
|
||||
if (dispatching_messages_)
|
||||
return;
|
||||
dispatching_messages_ = true;
|
||||
bool had_messages = false;
|
||||
do {
|
||||
if (dispatching_message_queue_.empty())
|
||||
SwapBehindLock(&incoming_message_queue_, &dispatching_message_queue_);
|
||||
had_messages = !dispatching_message_queue_.empty();
|
||||
while (!dispatching_message_queue_.empty()) {
|
||||
MessageQueue<InspectorAction>::value_type task;
|
||||
std::swap(dispatching_message_queue_.front(), task);
|
||||
dispatching_message_queue_.pop_front();
|
||||
StringView message = std::get<2>(task)->string();
|
||||
switch (std::get<0>(task)) {
|
||||
case InspectorAction::kStartSession:
|
||||
CHECK_EQ(session_delegate_, nullptr);
|
||||
session_id_ = std::get<1>(task);
|
||||
state_ = State::kConnected;
|
||||
SE_LOGD("Debugger attached.\n");
|
||||
session_delegate_ = std::unique_ptr<InspectorSessionDelegate>(
|
||||
new IoSessionDelegate(this));
|
||||
parent_env_->inspector_agent()->Connect(session_delegate_.get());
|
||||
break;
|
||||
case InspectorAction::kEndSession:
|
||||
CHECK_NE(session_delegate_, nullptr);
|
||||
if (state_ == State::kShutDown) {
|
||||
state_ = State::kDone;
|
||||
} else {
|
||||
state_ = State::kAccepting;
|
||||
}
|
||||
parent_env_->inspector_agent()->Disconnect();
|
||||
session_delegate_.reset();
|
||||
break;
|
||||
case InspectorAction::kSendMessage:
|
||||
parent_env_->inspector_agent()->Dispatch(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (had_messages);
|
||||
dispatching_messages_ = false;
|
||||
}
|
||||
|
||||
// static
|
||||
void InspectorIo::MainThreadReqAsyncCb(uv_async_t* req) {
|
||||
AsyncAndAgent* pair = node::ContainerOf(&AsyncAndAgent::first, req);
|
||||
// Note that this may be called after io was closed or even after a new
|
||||
// one was created and ran.
|
||||
InspectorIo* io = pair->second->io();
|
||||
if (io != nullptr)
|
||||
io->DispatchMessages();
|
||||
}
|
||||
|
||||
void InspectorIo::Write(TransportAction action, int session_id,
|
||||
const StringView& inspector_message) {
|
||||
AppendMessage(&outgoing_message_queue_, action, session_id,
|
||||
StringBuffer::create(inspector_message));
|
||||
int err = uv_async_send(&thread_req_);
|
||||
CHECK_EQ(0, err);
|
||||
}
|
||||
|
||||
InspectorIoDelegate::InspectorIoDelegate(InspectorIo* io,
|
||||
const std::string& script_path,
|
||||
const std::string& script_name,
|
||||
bool wait)
|
||||
: io_(io),
|
||||
connected_(false),
|
||||
session_id_(0),
|
||||
script_name_(script_name),
|
||||
script_path_(script_path),
|
||||
target_id_(GenerateID()),
|
||||
waiting_(wait) { }
|
||||
|
||||
|
||||
bool InspectorIoDelegate::StartSession(int session_id,
|
||||
const std::string& target_id) {
|
||||
if (connected_)
|
||||
return false;
|
||||
connected_ = true;
|
||||
session_id_++;
|
||||
io_->PostIncomingMessage(InspectorAction::kStartSession, session_id, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
void InspectorIoDelegate::MessageReceived(int session_id,
|
||||
const std::string& message) {
|
||||
// REFINE(pfeldman): Instead of blocking execution while debugger
|
||||
// engages, node should wait for the run callback from the remote client
|
||||
// and initiate its startup. This is a change to node.cc that should be
|
||||
// upstreamed separately.
|
||||
if (waiting_) {
|
||||
if (message.find("\"Runtime.runIfWaitingForDebugger\"") !=
|
||||
std::string::npos) {
|
||||
waiting_ = false;
|
||||
io_->ResumeStartup();
|
||||
}
|
||||
}
|
||||
io_->PostIncomingMessage(InspectorAction::kSendMessage, session_id,
|
||||
message);
|
||||
}
|
||||
|
||||
void InspectorIoDelegate::EndSession(int session_id) {
|
||||
connected_ = false;
|
||||
io_->PostIncomingMessage(InspectorAction::kEndSession, session_id, "");
|
||||
}
|
||||
|
||||
std::vector<std::string> InspectorIoDelegate::GetTargetIds() {
|
||||
return { target_id_ };
|
||||
}
|
||||
|
||||
std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) {
|
||||
return script_name_.empty() ? GetProcessTitle() : script_name_;
|
||||
}
|
||||
|
||||
std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) {
|
||||
return "file://" + script_path_;
|
||||
}
|
||||
|
||||
bool IoSessionDelegate::WaitForFrontendMessageWhilePaused() {
|
||||
io_->WaitForFrontendMessageWhilePaused();
|
||||
return true;
|
||||
}
|
||||
|
||||
void IoSessionDelegate::SendMessageToFrontend(
|
||||
const v8_inspector::StringView& message) {
|
||||
io_->Write(TransportAction::kSendMessage, io_->session_id_, message);
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,180 @@
|
||||
#ifndef SRC_INSPECTOR_IO_H_
|
||||
#define SRC_INSPECTOR_IO_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_socket_server.h"
|
||||
#include "node_debug_options.h"
|
||||
#include "node_mutex.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
|
||||
#if !HAVE_INSPECTOR
|
||||
#error("This header can only be used when inspector is enabled")
|
||||
#endif
|
||||
|
||||
|
||||
// Forward declaration to break recursive dependency chain with src/env.h.
|
||||
namespace node {
|
||||
class Environment;
|
||||
} // namespace node
|
||||
|
||||
namespace v8_inspector {
|
||||
class StringBuffer;
|
||||
class StringView;
|
||||
} // namespace v8_inspector
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
std::string FormatWsAddress(const std::string& host, int port,
|
||||
const std::string& target_id,
|
||||
bool include_protocol);
|
||||
|
||||
class InspectorIoDelegate;
|
||||
|
||||
enum class InspectorAction {
|
||||
kStartSession,
|
||||
kEndSession,
|
||||
kSendMessage
|
||||
};
|
||||
|
||||
// kKill closes connections and stops the server, kStop only stops the server
|
||||
enum class TransportAction {
|
||||
kKill,
|
||||
kSendMessage,
|
||||
kStop
|
||||
};
|
||||
|
||||
class InspectorIo {
|
||||
public:
|
||||
InspectorIo(node::Environment* env, v8::Platform* platform,
|
||||
const std::string& path, const DebugOptions& options,
|
||||
bool wait_for_connect);
|
||||
|
||||
~InspectorIo();
|
||||
// Start the inspector agent thread, waiting for it to initialize,
|
||||
// and waiting as well for a connection if wait_for_connect.
|
||||
bool Start();
|
||||
// Stop the inspector agent thread.
|
||||
void Stop();
|
||||
|
||||
bool IsStarted();
|
||||
bool IsConnected();
|
||||
|
||||
void WaitForDisconnect();
|
||||
// Called from thread to queue an incoming message and trigger
|
||||
// DispatchMessages() on the main thread.
|
||||
void PostIncomingMessage(InspectorAction action, int session_id,
|
||||
const std::string& message);
|
||||
void ResumeStartup() {
|
||||
uv_sem_post(&thread_start_sem_);
|
||||
}
|
||||
void ServerDone() {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&thread_req_), nullptr);
|
||||
}
|
||||
|
||||
int port() const { return port_; }
|
||||
std::string host() const { return options_.host_name(); }
|
||||
std::vector<std::string> GetTargetIds() const;
|
||||
|
||||
private:
|
||||
template <typename Action>
|
||||
using MessageQueue =
|
||||
std::deque<std::tuple<Action, int,
|
||||
std::unique_ptr<v8_inspector::StringBuffer>>>;
|
||||
enum class State {
|
||||
kNew,
|
||||
kAccepting,
|
||||
kConnected,
|
||||
kDone,
|
||||
kError,
|
||||
kShutDown
|
||||
};
|
||||
|
||||
// Callback for main_thread_req_'s uv_async_t
|
||||
static void MainThreadReqAsyncCb(uv_async_t* req);
|
||||
|
||||
// Wrapper for agent->ThreadMain()
|
||||
static void ThreadMain(void* agent);
|
||||
|
||||
// Runs a uv_loop_t
|
||||
template <typename Transport> void ThreadMain();
|
||||
// Called by ThreadMain's loop when triggered by thread_req_, writes
|
||||
// messages from outgoing_message_queue to the InspectorSockerServer
|
||||
template <typename Transport> static void IoThreadAsyncCb(uv_async_t* async);
|
||||
|
||||
void SetConnected(bool connected);
|
||||
void DispatchMessages();
|
||||
// Write action to outgoing_message_queue, and wake the thread
|
||||
void Write(TransportAction action, int session_id,
|
||||
const v8_inspector::StringView& message);
|
||||
// Thread-safe append of message to a queue. Return true if the queue
|
||||
// used to be empty.
|
||||
template <typename ActionType>
|
||||
bool AppendMessage(MessageQueue<ActionType>* vector, ActionType action,
|
||||
int session_id,
|
||||
std::unique_ptr<v8_inspector::StringBuffer> buffer);
|
||||
// Used as equivalent of a thread-safe "pop" of an entire queue's content.
|
||||
template <typename ActionType>
|
||||
void SwapBehindLock(MessageQueue<ActionType>* vector1,
|
||||
MessageQueue<ActionType>* vector2);
|
||||
// Wait on incoming_message_cond_
|
||||
void WaitForFrontendMessageWhilePaused();
|
||||
// Broadcast incoming_message_cond_
|
||||
void NotifyMessageReceived();
|
||||
|
||||
const DebugOptions options_;
|
||||
|
||||
// The IO thread runs its own uv_loop to implement the TCP server off
|
||||
// the main thread.
|
||||
uv_thread_t thread_;
|
||||
// Used by Start() to wait for thread to initialize, or for it to initialize
|
||||
// and receive a connection if wait_for_connect was requested.
|
||||
uv_sem_t thread_start_sem_;
|
||||
|
||||
InspectorIoDelegate* delegate_;
|
||||
State state_;
|
||||
node::Environment* parent_env_;
|
||||
|
||||
// Attached to the uv_loop in ThreadMain()
|
||||
uv_async_t thread_req_;
|
||||
// Note that this will live while the async is being closed - likely, past
|
||||
// the parent object lifespan
|
||||
std::pair<uv_async_t, Agent*>* main_thread_req_;
|
||||
std::unique_ptr<InspectorSessionDelegate> session_delegate_;
|
||||
v8::Platform* platform_;
|
||||
|
||||
// Message queues
|
||||
ConditionVariable incoming_message_cond_;
|
||||
Mutex state_lock_; // Locked before mutating either queue.
|
||||
MessageQueue<InspectorAction> incoming_message_queue_;
|
||||
MessageQueue<TransportAction> outgoing_message_queue_;
|
||||
MessageQueue<InspectorAction> dispatching_message_queue_;
|
||||
|
||||
bool dispatching_messages_;
|
||||
int session_id_;
|
||||
|
||||
std::string script_name_;
|
||||
std::string script_path_;
|
||||
const bool wait_for_connect_;
|
||||
int port_;
|
||||
|
||||
friend class DispatchMessagesTask;
|
||||
friend class IoSessionDelegate;
|
||||
friend void InterruptCallback(v8::Isolate*, void* agent);
|
||||
};
|
||||
|
||||
std::unique_ptr<v8_inspector::StringBuffer> Utf8ToStringView(
|
||||
const std::string& message);
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_INSPECTOR_IO_H_
|
||||
@@ -0,0 +1,640 @@
|
||||
#include "inspector_socket.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
//#include "openssl/sha.h" // Sha-1 hash
|
||||
#include "SHA1.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#define ACCEPT_KEY_LENGTH base64_encoded_size(20)
|
||||
#define BUFFER_GROWTH_CHUNK_SIZE 1024
|
||||
|
||||
#define DUMP_READS 0
|
||||
#define DUMP_WRITES 0
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
static const char CLOSE_FRAME[] = {'\x88', '\x00'};
|
||||
|
||||
enum ws_decode_result {
|
||||
FRAME_OK, FRAME_INCOMPLETE, FRAME_CLOSE, FRAME_ERROR
|
||||
};
|
||||
|
||||
#if DUMP_READS || DUMP_WRITES
|
||||
static void dump_hex(const char* buf, size_t len) {
|
||||
const char* ptr = buf;
|
||||
const char* end = ptr + len;
|
||||
const char* cptr;
|
||||
char c;
|
||||
int i;
|
||||
|
||||
while (ptr < end) {
|
||||
cptr = ptr;
|
||||
for (i = 0; i < 16 && ptr < end; i++) {
|
||||
printf("%2.2X ", static_cast<unsigned char>(*(ptr++)));
|
||||
}
|
||||
for (i = 72 - (i * 4); i > 0; i--) {
|
||||
printf(" ");
|
||||
}
|
||||
for (i = 0; i < 16 && cptr < end; i++) {
|
||||
c = *(cptr++);
|
||||
printf("%c", (c > 0x19) ? c : '.');
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void remove_from_beginning(std::vector<char>* buffer, size_t count) {
|
||||
buffer->erase(buffer->begin(), buffer->begin() + count);
|
||||
}
|
||||
|
||||
static void dispose_inspector(uv_handle_t* handle) {
|
||||
InspectorSocket* inspector = inspector_from_stream(handle);
|
||||
inspector_cb close =
|
||||
inspector->ws_mode ? inspector->ws_state->close_cb : nullptr;
|
||||
inspector->buffer.clear();
|
||||
delete inspector->ws_state;
|
||||
inspector->ws_state = nullptr;
|
||||
if (close) {
|
||||
close(inspector, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_connection(InspectorSocket* inspector) {
|
||||
uv_handle_t* socket = reinterpret_cast<uv_handle_t*>(&inspector->tcp);
|
||||
if (!uv_is_closing(socket)) {
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(socket));
|
||||
uv_close(socket, dispose_inspector);
|
||||
}
|
||||
}
|
||||
|
||||
struct WriteRequest {
|
||||
WriteRequest(InspectorSocket* inspector, const char* data, size_t size)
|
||||
: inspector(inspector)
|
||||
, storage(data, data + size)
|
||||
, buf(uv_buf_init(&storage[0], storage.size())) {}
|
||||
|
||||
static WriteRequest* from_write_req(uv_write_t* req) {
|
||||
return node::ContainerOf(&WriteRequest::req, req);
|
||||
}
|
||||
|
||||
InspectorSocket* const inspector;
|
||||
std::vector<char> storage;
|
||||
uv_write_t req;
|
||||
uv_buf_t buf;
|
||||
};
|
||||
|
||||
// Cleanup
|
||||
static void write_request_cleanup(uv_write_t* req, int status) {
|
||||
delete WriteRequest::from_write_req(req);
|
||||
}
|
||||
|
||||
static int write_to_client(InspectorSocket* inspector,
|
||||
const char* msg,
|
||||
size_t len,
|
||||
uv_write_cb write_cb = write_request_cleanup) {
|
||||
#if DUMP_WRITES
|
||||
printf("%s (%ld bytes):\n", __FUNCTION__, len);
|
||||
dump_hex(msg, len);
|
||||
#endif
|
||||
|
||||
// Freed in write_request_cleanup
|
||||
WriteRequest* wr = new WriteRequest(inspector, msg, len);
|
||||
uv_stream_t* stream = reinterpret_cast<uv_stream_t*>(&inspector->tcp);
|
||||
return uv_write(&wr->req, stream, &wr->buf, 1, write_cb) < 0;
|
||||
}
|
||||
|
||||
// Constants for hybi-10 frame format.
|
||||
|
||||
typedef int OpCode;
|
||||
|
||||
const OpCode kOpCodeContinuation = 0x0;
|
||||
const OpCode kOpCodeText = 0x1;
|
||||
const OpCode kOpCodeBinary = 0x2;
|
||||
const OpCode kOpCodeClose = 0x8;
|
||||
const OpCode kOpCodePing = 0x9;
|
||||
const OpCode kOpCodePong = 0xA;
|
||||
|
||||
const unsigned char kFinalBit = 0x80;
|
||||
const unsigned char kReserved1Bit = 0x40;
|
||||
const unsigned char kReserved2Bit = 0x20;
|
||||
const unsigned char kReserved3Bit = 0x10;
|
||||
const unsigned char kOpCodeMask = 0xF;
|
||||
const unsigned char kMaskBit = 0x80;
|
||||
const unsigned char kPayloadLengthMask = 0x7F;
|
||||
|
||||
const size_t kMaxSingleBytePayloadLength = 125;
|
||||
const size_t kTwoBytePayloadLengthField = 126;
|
||||
const size_t kEightBytePayloadLengthField = 127;
|
||||
const size_t kMaskingKeyWidthInBytes = 4;
|
||||
|
||||
static std::vector<char> encode_frame_hybi17(const char* message,
|
||||
size_t data_length) {
|
||||
std::vector<char> frame;
|
||||
OpCode op_code = kOpCodeText;
|
||||
frame.push_back(kFinalBit | op_code);
|
||||
if (data_length <= kMaxSingleBytePayloadLength) {
|
||||
frame.push_back(static_cast<char>(data_length));
|
||||
} else if (data_length <= 0xFFFF) {
|
||||
frame.push_back(kTwoBytePayloadLengthField);
|
||||
frame.push_back((data_length & 0xFF00) >> 8);
|
||||
frame.push_back(data_length & 0xFF);
|
||||
} else {
|
||||
frame.push_back(kEightBytePayloadLengthField);
|
||||
char extended_payload_length[8];
|
||||
size_t remaining = data_length;
|
||||
// Fill the length into extended_payload_length in the network byte order.
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
extended_payload_length[7 - i] = remaining & 0xFF;
|
||||
remaining >>= 8;
|
||||
}
|
||||
frame.insert(frame.end(), extended_payload_length,
|
||||
extended_payload_length + 8);
|
||||
CHECK_EQ(0, remaining);
|
||||
}
|
||||
frame.insert(frame.end(), message, message + data_length);
|
||||
return frame;
|
||||
}
|
||||
|
||||
static ws_decode_result decode_frame_hybi17(const std::vector<char>& buffer,
|
||||
bool client_frame,
|
||||
int* bytes_consumed,
|
||||
std::vector<char>* output,
|
||||
bool* compressed) {
|
||||
*bytes_consumed = 0;
|
||||
if (buffer.size() < 2)
|
||||
return FRAME_INCOMPLETE;
|
||||
|
||||
auto it = buffer.begin();
|
||||
|
||||
unsigned char first_byte = *it++;
|
||||
unsigned char second_byte = *it++;
|
||||
|
||||
bool final = (first_byte & kFinalBit) != 0;
|
||||
bool reserved1 = (first_byte & kReserved1Bit) != 0;
|
||||
bool reserved2 = (first_byte & kReserved2Bit) != 0;
|
||||
bool reserved3 = (first_byte & kReserved3Bit) != 0;
|
||||
int op_code = first_byte & kOpCodeMask;
|
||||
bool masked = (second_byte & kMaskBit) != 0;
|
||||
*compressed = reserved1;
|
||||
if (!final || reserved2 || reserved3)
|
||||
return FRAME_ERROR; // Only compression extension is supported.
|
||||
|
||||
bool closed = false;
|
||||
switch (op_code) {
|
||||
case kOpCodeClose:
|
||||
closed = true;
|
||||
break;
|
||||
case kOpCodeText:
|
||||
break;
|
||||
case kOpCodeBinary: // We don't support binary frames yet.
|
||||
case kOpCodeContinuation: // We don't support binary frames yet.
|
||||
case kOpCodePing: // We don't support binary frames yet.
|
||||
case kOpCodePong: // We don't support binary frames yet.
|
||||
default:
|
||||
return FRAME_ERROR;
|
||||
}
|
||||
|
||||
// In Hybi-17 spec client MUST mask its frame.
|
||||
if (client_frame && !masked) {
|
||||
return FRAME_ERROR;
|
||||
}
|
||||
|
||||
uint64_t payload_length64 = second_byte & kPayloadLengthMask;
|
||||
if (payload_length64 > kMaxSingleBytePayloadLength) {
|
||||
int extended_payload_length_size;
|
||||
if (payload_length64 == kTwoBytePayloadLengthField) {
|
||||
extended_payload_length_size = 2;
|
||||
} else if (payload_length64 == kEightBytePayloadLengthField) {
|
||||
extended_payload_length_size = 8;
|
||||
} else {
|
||||
return FRAME_ERROR;
|
||||
}
|
||||
if ((buffer.end() - it) < extended_payload_length_size)
|
||||
return FRAME_INCOMPLETE;
|
||||
payload_length64 = 0;
|
||||
for (int i = 0; i < extended_payload_length_size; ++i) {
|
||||
payload_length64 <<= 8;
|
||||
payload_length64 |= static_cast<unsigned char>(*it++);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull;
|
||||
static const size_t max_length = SIZE_MAX;
|
||||
if (payload_length64 > max_payload_length ||
|
||||
payload_length64 > max_length - kMaskingKeyWidthInBytes) {
|
||||
// WebSocket frame length too large.
|
||||
return FRAME_ERROR;
|
||||
}
|
||||
size_t payload_length = static_cast<size_t>(payload_length64);
|
||||
|
||||
if (buffer.size() - kMaskingKeyWidthInBytes < payload_length)
|
||||
return FRAME_INCOMPLETE;
|
||||
|
||||
std::vector<char>::const_iterator masking_key = it;
|
||||
std::vector<char>::const_iterator payload = it + kMaskingKeyWidthInBytes;
|
||||
for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
|
||||
output->insert(output->end(),
|
||||
payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes]);
|
||||
|
||||
size_t pos = it + kMaskingKeyWidthInBytes + payload_length - buffer.begin();
|
||||
*bytes_consumed = pos;
|
||||
return closed ? FRAME_CLOSE : FRAME_OK;
|
||||
}
|
||||
|
||||
static void invoke_read_callback(InspectorSocket* inspector,
|
||||
int status, const uv_buf_t* buf) {
|
||||
if (inspector->ws_state->read_cb) {
|
||||
inspector->ws_state->read_cb(
|
||||
reinterpret_cast<uv_stream_t*>(&inspector->tcp), status, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdown_complete(InspectorSocket* inspector) {
|
||||
close_connection(inspector);
|
||||
}
|
||||
|
||||
static void on_close_frame_written(uv_write_t* req, int status) {
|
||||
WriteRequest* wr = WriteRequest::from_write_req(req);
|
||||
InspectorSocket* inspector = wr->inspector;
|
||||
delete wr;
|
||||
inspector->ws_state->close_sent = true;
|
||||
if (inspector->ws_state->received_close) {
|
||||
shutdown_complete(inspector);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_frame_received(InspectorSocket* inspector) {
|
||||
inspector->ws_state->received_close = true;
|
||||
if (!inspector->ws_state->close_sent) {
|
||||
invoke_read_callback(inspector, 0, 0);
|
||||
write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME),
|
||||
on_close_frame_written);
|
||||
} else {
|
||||
shutdown_complete(inspector);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_ws_frames(InspectorSocket* inspector) {
|
||||
int bytes_consumed = 0;
|
||||
std::vector<char> output;
|
||||
bool compressed = false;
|
||||
|
||||
ws_decode_result r = decode_frame_hybi17(inspector->buffer,
|
||||
true /* client_frame */,
|
||||
&bytes_consumed, &output,
|
||||
&compressed);
|
||||
// Compressed frame means client is ignoring the headers and misbehaves
|
||||
if (compressed || r == FRAME_ERROR) {
|
||||
invoke_read_callback(inspector, UV_EPROTO, nullptr);
|
||||
close_connection(inspector);
|
||||
bytes_consumed = 0;
|
||||
} else if (r == FRAME_CLOSE) {
|
||||
close_frame_received(inspector);
|
||||
bytes_consumed = 0;
|
||||
} else if (r == FRAME_OK && inspector->ws_state->alloc_cb
|
||||
&& inspector->ws_state->read_cb) {
|
||||
uv_buf_t buffer;
|
||||
size_t len = output.size();
|
||||
inspector->ws_state->alloc_cb(
|
||||
reinterpret_cast<uv_handle_t*>(&inspector->tcp),
|
||||
len, &buffer);
|
||||
CHECK_GE(buffer.len, len);
|
||||
memcpy(buffer.base, &output[0], len);
|
||||
invoke_read_callback(inspector, len, &buffer);
|
||||
}
|
||||
return bytes_consumed;
|
||||
}
|
||||
|
||||
static void prepare_buffer(uv_handle_t* stream, size_t len, uv_buf_t* buf) {
|
||||
*buf = uv_buf_init(new char[len], len);
|
||||
}
|
||||
|
||||
static void reclaim_uv_buf(InspectorSocket* inspector, const uv_buf_t* buf,
|
||||
ssize_t read) {
|
||||
if (read > 0) {
|
||||
std::vector<char>& buffer = inspector->buffer;
|
||||
buffer.insert(buffer.end(), buf->base, buf->base + read);
|
||||
}
|
||||
delete[] buf->base;
|
||||
}
|
||||
|
||||
static void websockets_data_cb(uv_stream_t* stream, ssize_t nread,
|
||||
const uv_buf_t* buf) {
|
||||
InspectorSocket* inspector = inspector_from_stream(stream);
|
||||
reclaim_uv_buf(inspector, buf, nread);
|
||||
if (nread < 0 || nread == UV_EOF) {
|
||||
inspector->connection_eof = true;
|
||||
if (!inspector->shutting_down && inspector->ws_state->read_cb) {
|
||||
inspector->ws_state->read_cb(stream, nread, nullptr);
|
||||
}
|
||||
if (inspector->ws_state->close_sent &&
|
||||
!inspector->ws_state->received_close) {
|
||||
shutdown_complete(inspector); // invoke callback
|
||||
}
|
||||
} else {
|
||||
#if DUMP_READS
|
||||
printf("%s read %ld bytes\n", __FUNCTION__, nread);
|
||||
if (nread > 0) {
|
||||
dump_hex(inspector->buffer.data() + inspector->buffer.size() - nread,
|
||||
nread);
|
||||
}
|
||||
#endif
|
||||
// 2. Parse.
|
||||
int processed = 0;
|
||||
do {
|
||||
processed = parse_ws_frames(inspector);
|
||||
// 3. Fix the buffer size & length
|
||||
if (processed > 0) {
|
||||
remove_from_beginning(&inspector->buffer, processed);
|
||||
}
|
||||
} while (processed > 0 && !inspector->buffer.empty());
|
||||
}
|
||||
}
|
||||
|
||||
int inspector_read_start(InspectorSocket* inspector,
|
||||
uv_alloc_cb alloc_cb, uv_read_cb read_cb) {
|
||||
CHECK(inspector->ws_mode);
|
||||
CHECK(!inspector->shutting_down || read_cb == nullptr);
|
||||
inspector->ws_state->close_sent = false;
|
||||
inspector->ws_state->alloc_cb = alloc_cb;
|
||||
inspector->ws_state->read_cb = read_cb;
|
||||
int err =
|
||||
uv_read_start(reinterpret_cast<uv_stream_t*>(&inspector->tcp),
|
||||
prepare_buffer,
|
||||
websockets_data_cb);
|
||||
if (err < 0) {
|
||||
close_connection(inspector);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void inspector_read_stop(InspectorSocket* inspector) {
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(&inspector->tcp));
|
||||
inspector->ws_state->alloc_cb = nullptr;
|
||||
inspector->ws_state->read_cb = nullptr;
|
||||
}
|
||||
|
||||
static void generate_accept_string(const std::string& client_key,
|
||||
char (*buffer)[ACCEPT_KEY_LENGTH]) {
|
||||
// Magic string from websockets spec.
|
||||
static const char ws_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
std::string input(client_key + ws_magic);
|
||||
// char hash[SHA_DIGEST_LENGTH];
|
||||
// SHA1(reinterpret_cast<const unsigned char*>(&input[0]), input.size(),
|
||||
// reinterpret_cast<unsigned char*>(hash));
|
||||
|
||||
se::SHA1Sum::Hash hash = {0};
|
||||
se::SHA1Sum s;
|
||||
s.update(reinterpret_cast<const unsigned char*>(&input[0]), (uint32_t)input.size());
|
||||
s.finish(hash);
|
||||
|
||||
node::base64_encode((char*)hash, sizeof(hash), *buffer, sizeof(*buffer));
|
||||
}
|
||||
|
||||
static int header_value_cb(http_parser* parser, const char* at, size_t length) {
|
||||
static const char SEC_WEBSOCKET_KEY_HEADER[] = "Sec-WebSocket-Key";
|
||||
auto inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
auto state = inspector->http_parsing_state;
|
||||
state->parsing_value = true;
|
||||
if (state->current_header.size() == sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1 &&
|
||||
node::StringEqualNoCaseN(state->current_header.data(),
|
||||
SEC_WEBSOCKET_KEY_HEADER,
|
||||
sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1)) {
|
||||
state->ws_key.append(at, length);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int header_field_cb(http_parser* parser, const char* at, size_t length) {
|
||||
auto inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
auto state = inspector->http_parsing_state;
|
||||
if (state->parsing_value) {
|
||||
state->parsing_value = false;
|
||||
state->current_header.clear();
|
||||
}
|
||||
state->current_header.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int path_cb(http_parser* parser, const char* at, size_t length) {
|
||||
auto inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
auto state = inspector->http_parsing_state;
|
||||
state->path.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handshake_complete(InspectorSocket* inspector) {
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(&inspector->tcp));
|
||||
handshake_cb callback = inspector->http_parsing_state->callback;
|
||||
inspector->ws_state = new ws_state_s();
|
||||
inspector->ws_mode = true;
|
||||
callback(inspector, kInspectorHandshakeUpgraded,
|
||||
inspector->http_parsing_state->path);
|
||||
}
|
||||
|
||||
static void cleanup_http_parsing_state(InspectorSocket* inspector) {
|
||||
delete inspector->http_parsing_state;
|
||||
inspector->http_parsing_state = nullptr;
|
||||
}
|
||||
|
||||
static void report_handshake_failure_cb(uv_handle_t* handle) {
|
||||
dispose_inspector(handle);
|
||||
InspectorSocket* inspector = inspector_from_stream(handle);
|
||||
handshake_cb cb = inspector->http_parsing_state->callback;
|
||||
cleanup_http_parsing_state(inspector);
|
||||
cb(inspector, kInspectorHandshakeFailed, std::string());
|
||||
}
|
||||
|
||||
static void close_and_report_handshake_failure(InspectorSocket* inspector) {
|
||||
uv_handle_t* socket = reinterpret_cast<uv_handle_t*>(&inspector->tcp);
|
||||
if (uv_is_closing(socket)) {
|
||||
report_handshake_failure_cb(socket);
|
||||
} else {
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(socket));
|
||||
uv_close(socket, report_handshake_failure_cb);
|
||||
}
|
||||
}
|
||||
|
||||
static void then_close_and_report_failure(uv_write_t* req, int status) {
|
||||
InspectorSocket* inspector = WriteRequest::from_write_req(req)->inspector;
|
||||
write_request_cleanup(req, status);
|
||||
close_and_report_handshake_failure(inspector);
|
||||
}
|
||||
|
||||
static void handshake_failed(InspectorSocket* inspector) {
|
||||
const char HANDSHAKE_FAILED_RESPONSE[] =
|
||||
"HTTP/1.0 400 Bad Request\r\n"
|
||||
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
|
||||
"WebSockets request was expected\r\n";
|
||||
write_to_client(inspector, HANDSHAKE_FAILED_RESPONSE,
|
||||
sizeof(HANDSHAKE_FAILED_RESPONSE) - 1,
|
||||
then_close_and_report_failure);
|
||||
}
|
||||
|
||||
// init_handshake references message_complete_cb
|
||||
static void init_handshake(InspectorSocket* socket);
|
||||
|
||||
static int message_complete_cb(http_parser* parser) {
|
||||
InspectorSocket* inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
struct http_parsing_state_s* state = inspector->http_parsing_state;
|
||||
if (parser->method != HTTP_GET) {
|
||||
handshake_failed(inspector);
|
||||
} else if (!parser->upgrade) {
|
||||
if (state->callback(inspector, kInspectorHandshakeHttpGet, state->path)) {
|
||||
init_handshake(inspector);
|
||||
} else {
|
||||
handshake_failed(inspector);
|
||||
}
|
||||
} else if (state->ws_key.empty()) {
|
||||
handshake_failed(inspector);
|
||||
} else if (state->callback(inspector, kInspectorHandshakeUpgrading,
|
||||
state->path)) {
|
||||
char accept_string[ACCEPT_KEY_LENGTH];
|
||||
generate_accept_string(state->ws_key, &accept_string);
|
||||
const char accept_ws_prefix[] = "HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Accept: ";
|
||||
const char accept_ws_suffix[] = "\r\n\r\n";
|
||||
std::string reply(accept_ws_prefix, sizeof(accept_ws_prefix) - 1);
|
||||
reply.append(accept_string, sizeof(accept_string));
|
||||
reply.append(accept_ws_suffix, sizeof(accept_ws_suffix) - 1);
|
||||
if (write_to_client(inspector, &reply[0], reply.size()) >= 0) {
|
||||
handshake_complete(inspector);
|
||||
inspector->http_parsing_state->done = true;
|
||||
} else {
|
||||
close_and_report_handshake_failure(inspector);
|
||||
}
|
||||
} else {
|
||||
handshake_failed(inspector);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void data_received_cb(uv_stream_s* tcp, ssize_t nread,
|
||||
const uv_buf_t* buf) {
|
||||
#if DUMP_READS
|
||||
if (nread >= 0) {
|
||||
printf("%s (%ld bytes)\n", __FUNCTION__, nread);
|
||||
dump_hex(buf->base, nread);
|
||||
} else {
|
||||
printf("[%s:%d] %s\n", __FUNCTION__, __LINE__, uv_err_name(nread));
|
||||
}
|
||||
#endif
|
||||
InspectorSocket* inspector = inspector_from_stream(tcp);
|
||||
reclaim_uv_buf(inspector, buf, nread);
|
||||
if (nread < 0 || nread == UV_EOF) {
|
||||
close_and_report_handshake_failure(inspector);
|
||||
} else {
|
||||
http_parsing_state_s* state = inspector->http_parsing_state;
|
||||
http_parser* parser = &state->parser;
|
||||
http_parser_execute(parser, &state->parser_settings,
|
||||
inspector->buffer.data(), nread);
|
||||
remove_from_beginning(&inspector->buffer, nread);
|
||||
if (parser->http_errno != HPE_OK) {
|
||||
handshake_failed(inspector);
|
||||
}
|
||||
if (inspector->http_parsing_state->done) {
|
||||
cleanup_http_parsing_state(inspector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_handshake(InspectorSocket* socket) {
|
||||
http_parsing_state_s* state = socket->http_parsing_state;
|
||||
CHECK_NE(state, nullptr);
|
||||
state->current_header.clear();
|
||||
state->ws_key.clear();
|
||||
state->path.clear();
|
||||
state->done = false;
|
||||
http_parser_init(&state->parser, HTTP_REQUEST);
|
||||
state->parser.data = socket;
|
||||
http_parser_settings* settings = &state->parser_settings;
|
||||
http_parser_settings_init(settings);
|
||||
settings->on_header_field = header_field_cb;
|
||||
settings->on_header_value = header_value_cb;
|
||||
settings->on_message_complete = message_complete_cb;
|
||||
settings->on_url = path_cb;
|
||||
}
|
||||
|
||||
int inspector_accept(uv_stream_t* server, InspectorSocket* socket,
|
||||
handshake_cb callback) {
|
||||
CHECK_NE(callback, nullptr);
|
||||
CHECK_EQ(socket->http_parsing_state, nullptr);
|
||||
|
||||
socket->http_parsing_state = new http_parsing_state_s();
|
||||
uv_stream_t* tcp = reinterpret_cast<uv_stream_t*>(&socket->tcp);
|
||||
int err = uv_tcp_init(server->loop, &socket->tcp);
|
||||
|
||||
if (err == 0) {
|
||||
err = uv_accept(server, tcp);
|
||||
}
|
||||
if (err == 0) {
|
||||
init_handshake(socket);
|
||||
socket->http_parsing_state->callback = callback;
|
||||
err = uv_read_start(tcp, prepare_buffer,
|
||||
data_received_cb);
|
||||
}
|
||||
if (err != 0) {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(tcp), NULL);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void inspector_write(InspectorSocket* inspector, const char* data,
|
||||
size_t len) {
|
||||
if (inspector->ws_mode) {
|
||||
std::vector<char> output = encode_frame_hybi17(data, len);
|
||||
write_to_client(inspector, &output[0], output.size());
|
||||
} else {
|
||||
write_to_client(inspector, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void inspector_close(InspectorSocket* inspector,
|
||||
inspector_cb callback) {
|
||||
// libuv throws assertions when closing stream that's already closed - we
|
||||
// need to do the same.
|
||||
CHECK(!uv_is_closing(reinterpret_cast<uv_handle_t*>(&inspector->tcp)));
|
||||
CHECK(!inspector->shutting_down);
|
||||
inspector->shutting_down = true;
|
||||
inspector->ws_state->close_cb = callback;
|
||||
if (inspector->connection_eof) {
|
||||
close_connection(inspector);
|
||||
} else {
|
||||
inspector_read_stop(inspector);
|
||||
write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME),
|
||||
on_close_frame_written);
|
||||
inspector_read_start(inspector, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool inspector_is_active(const InspectorSocket* inspector) {
|
||||
const uv_handle_t* tcp =
|
||||
reinterpret_cast<const uv_handle_t*>(&inspector->tcp);
|
||||
return !inspector->shutting_down && !uv_is_closing(tcp);
|
||||
}
|
||||
|
||||
void InspectorSocket::reinit() {
|
||||
http_parsing_state = nullptr;
|
||||
ws_state = nullptr;
|
||||
buffer.clear();
|
||||
ws_mode = false;
|
||||
shutting_down = false;
|
||||
connection_eof = false;
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef SRC_INSPECTOR_SOCKET_H_
|
||||
#define SRC_INSPECTOR_SOCKET_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "http_parser.h"
|
||||
#include "util.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
enum inspector_handshake_event {
|
||||
kInspectorHandshakeUpgrading,
|
||||
kInspectorHandshakeUpgraded,
|
||||
kInspectorHandshakeHttpGet,
|
||||
kInspectorHandshakeFailed
|
||||
};
|
||||
|
||||
class InspectorSocket;
|
||||
|
||||
typedef void (*inspector_cb)(InspectorSocket*, int);
|
||||
// Notifies as handshake is progressing. Returning false as a response to
|
||||
// kInspectorHandshakeUpgrading or kInspectorHandshakeHttpGet event will abort
|
||||
// the connection. inspector_write can be used from the callback.
|
||||
typedef bool (*handshake_cb)(InspectorSocket*,
|
||||
enum inspector_handshake_event state,
|
||||
const std::string& path);
|
||||
|
||||
struct http_parsing_state_s {
|
||||
http_parser parser;
|
||||
http_parser_settings parser_settings;
|
||||
handshake_cb callback;
|
||||
bool done;
|
||||
bool parsing_value;
|
||||
std::string ws_key;
|
||||
std::string path;
|
||||
std::string current_header;
|
||||
};
|
||||
|
||||
struct ws_state_s {
|
||||
uv_alloc_cb alloc_cb;
|
||||
uv_read_cb read_cb;
|
||||
inspector_cb close_cb;
|
||||
bool close_sent;
|
||||
bool received_close;
|
||||
};
|
||||
|
||||
// HTTP Wrapper around a uv_tcp_t
|
||||
class InspectorSocket {
|
||||
public:
|
||||
InspectorSocket() : data(nullptr), http_parsing_state(nullptr),
|
||||
ws_state(nullptr), buffer(0), ws_mode(false),
|
||||
shutting_down(false), connection_eof(false) { }
|
||||
void reinit();
|
||||
void* data;
|
||||
struct http_parsing_state_s* http_parsing_state;
|
||||
struct ws_state_s* ws_state;
|
||||
std::vector<char> buffer;
|
||||
uv_tcp_t tcp;
|
||||
bool ws_mode;
|
||||
bool shutting_down;
|
||||
bool connection_eof;
|
||||
|
||||
private:
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(InspectorSocket);
|
||||
};
|
||||
|
||||
int inspector_accept(uv_stream_t* server, InspectorSocket* inspector,
|
||||
handshake_cb callback);
|
||||
|
||||
void inspector_close(InspectorSocket* inspector,
|
||||
inspector_cb callback);
|
||||
|
||||
// Callbacks will receive stream handles. Use inspector_from_stream to get
|
||||
// InspectorSocket* from the stream handle.
|
||||
int inspector_read_start(InspectorSocket* inspector, uv_alloc_cb,
|
||||
uv_read_cb);
|
||||
void inspector_read_stop(InspectorSocket* inspector);
|
||||
void inspector_write(InspectorSocket* inspector,
|
||||
const char* data, size_t len);
|
||||
bool inspector_is_active(const InspectorSocket* inspector);
|
||||
|
||||
inline InspectorSocket* inspector_from_stream(uv_tcp_t* stream) {
|
||||
return node::ContainerOf(&InspectorSocket::tcp, stream);
|
||||
}
|
||||
|
||||
inline InspectorSocket* inspector_from_stream(uv_stream_t* stream) {
|
||||
return inspector_from_stream(reinterpret_cast<uv_tcp_t*>(stream));
|
||||
}
|
||||
|
||||
inline InspectorSocket* inspector_from_stream(uv_handle_t* stream) {
|
||||
return inspector_from_stream(reinterpret_cast<uv_tcp_t*>(stream));
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_INSPECTOR_SOCKET_H_
|
||||
@@ -0,0 +1,652 @@
|
||||
#include "inspector_socket_server.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "node.h"
|
||||
#include "uv.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
// Function is declared in inspector_io.h so the rest of the node does not
|
||||
// depend on inspector_socket_server.h
|
||||
std::string FormatWsAddress(const std::string& host, int port,
|
||||
const std::string& target_id,
|
||||
bool include_protocol) {
|
||||
// Host is valid (socket was bound) so colon means it's a v6 IP address
|
||||
bool v6 = host.find(':') != std::string::npos;
|
||||
std::ostringstream url;
|
||||
if (include_protocol)
|
||||
url << "ws://";
|
||||
if (v6) {
|
||||
url << '[';
|
||||
}
|
||||
url << host;
|
||||
if (v6) {
|
||||
url << ']';
|
||||
}
|
||||
url << ':' << port << '/' << target_id;
|
||||
return url.str();
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
static const uint8_t PROTOCOL_JSON[] = {
|
||||
#include "v8_inspector_protocol_json.h" // NOLINT(build/include_order)
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static std::string to_string(T arg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << arg;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void Escape(std::string* string) {
|
||||
for (char& c : *string) {
|
||||
c = (c == '\"' || c == '\\') ? '_' : c;
|
||||
}
|
||||
}
|
||||
|
||||
std::string MapToString(const std::map<std::string, std::string>& object) {
|
||||
bool first = true;
|
||||
std::ostringstream json;
|
||||
json << "{\n";
|
||||
for (const auto& name_value : object) {
|
||||
if (!first)
|
||||
json << ",\n";
|
||||
first = false;
|
||||
json << " \"" << name_value.first << "\": \"";
|
||||
json << name_value.second << "\"";
|
||||
}
|
||||
json << "\n} ";
|
||||
return json.str();
|
||||
}
|
||||
|
||||
std::string MapsToString(
|
||||
const std::vector<std::map<std::string, std::string>>& array) {
|
||||
bool first = true;
|
||||
std::ostringstream json;
|
||||
json << "[ ";
|
||||
for (const auto& object : array) {
|
||||
if (!first)
|
||||
json << ", ";
|
||||
first = false;
|
||||
json << MapToString(object);
|
||||
}
|
||||
json << "]\n\n";
|
||||
return json.str();
|
||||
}
|
||||
|
||||
const char* MatchPathSegment(const char* path, const char* expected) {
|
||||
size_t len = strlen(expected);
|
||||
if (StringEqualNoCaseN(path, expected, len)) {
|
||||
if (path[len] == '/') return path + len + 1;
|
||||
if (path[len] == '\0') return path + len;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) {
|
||||
buf->base = new char[len];
|
||||
buf->len = len;
|
||||
}
|
||||
|
||||
void PrintDebuggerReadyMessage(const std::string& host,
|
||||
int port,
|
||||
const std::vector<std::string>& ids,
|
||||
FILE* out) {
|
||||
if (out == NULL) {
|
||||
return;
|
||||
}
|
||||
for (const std::string& id : ids) {
|
||||
SE_LOGD("Debugger listening..., visit [ devtools://devtools/bundled/js_app.html?v8only=true&ws=%s ] in chrome browser to debug!\n",
|
||||
FormatWsAddress(host, port, id, false).c_str());
|
||||
}
|
||||
SE_LOGD("For help see %s\n",
|
||||
"https://nodejs.org/en/docs/inspector");
|
||||
}
|
||||
|
||||
void SendHttpResponse(InspectorSocket* socket, const std::string& response) {
|
||||
const char HEADERS[] = "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: application/json; charset=UTF-8\r\n"
|
||||
"Cache-Control: no-cache\r\n"
|
||||
"Content-Length: %zu\r\n"
|
||||
"\r\n";
|
||||
char header[sizeof(HEADERS) + 20];
|
||||
int header_len = snprintf(header, sizeof(header), HEADERS, response.size());
|
||||
inspector_write(socket, header, header_len);
|
||||
inspector_write(socket, response.data(), response.size());
|
||||
}
|
||||
|
||||
void SendVersionResponse(InspectorSocket* socket) {
|
||||
std::map<std::string, std::string> response;
|
||||
response["Browser"] = "Cocos2d-x Games"; //cjh
|
||||
response["Protocol-Version"] = "1.1";
|
||||
SendHttpResponse(socket, MapToString(response));
|
||||
}
|
||||
|
||||
void SendProtocolJson(InspectorSocket* socket) {
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
CHECK_EQ(Z_OK, inflateInit(&strm));
|
||||
static const size_t kDecompressedSize =
|
||||
PROTOCOL_JSON[0] * 0x10000u +
|
||||
PROTOCOL_JSON[1] * 0x100u +
|
||||
PROTOCOL_JSON[2];
|
||||
strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + 3);
|
||||
strm.avail_in = sizeof(PROTOCOL_JSON) - 3;
|
||||
std::string data(kDecompressedSize, '\0');
|
||||
strm.next_out = reinterpret_cast<Byte*>(&data[0]);
|
||||
strm.avail_out = data.size();
|
||||
CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH));
|
||||
CHECK_EQ(0, strm.avail_out);
|
||||
CHECK_EQ(Z_OK, inflateEnd(&strm));
|
||||
SendHttpResponse(socket, data);
|
||||
}
|
||||
|
||||
int GetSocketHost(uv_tcp_t* socket, std::string* out_host) {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
sockaddr_storage addr;
|
||||
int len = sizeof(addr);
|
||||
int err = uv_tcp_getsockname(socket,
|
||||
reinterpret_cast<struct sockaddr*>(&addr),
|
||||
&len);
|
||||
if (err != 0)
|
||||
return err;
|
||||
if (addr.ss_family == AF_INET6) {
|
||||
const sockaddr_in6* v6 = reinterpret_cast<const sockaddr_in6*>(&addr);
|
||||
err = uv_ip6_name(v6, ip, sizeof(ip));
|
||||
} else {
|
||||
const sockaddr_in* v4 = reinterpret_cast<const sockaddr_in*>(&addr);
|
||||
err = uv_ip4_name(v4, ip, sizeof(ip));
|
||||
}
|
||||
if (err != 0)
|
||||
return err;
|
||||
*out_host = ip;
|
||||
return err;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
class Closer {
|
||||
public:
|
||||
explicit Closer(InspectorSocketServer* server) : server_(server),
|
||||
close_count_(0) { }
|
||||
|
||||
void AddCallback(InspectorSocketServer::ServerCallback callback) {
|
||||
if (callback == nullptr)
|
||||
return;
|
||||
callbacks_.insert(callback);
|
||||
}
|
||||
|
||||
void DecreaseExpectedCount() {
|
||||
--close_count_;
|
||||
NotifyIfDone();
|
||||
}
|
||||
|
||||
void IncreaseExpectedCount() {
|
||||
++close_count_;
|
||||
}
|
||||
|
||||
void NotifyIfDone() {
|
||||
if (close_count_ == 0) {
|
||||
for (auto callback : callbacks_) {
|
||||
callback(server_);
|
||||
}
|
||||
InspectorSocketServer* server = server_;
|
||||
delete server->closer_;
|
||||
server->closer_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
InspectorSocketServer* server_;
|
||||
std::set<InspectorSocketServer::ServerCallback> callbacks_;
|
||||
int close_count_;
|
||||
};
|
||||
|
||||
class SocketSession {
|
||||
public:
|
||||
static int Accept(InspectorSocketServer* server, int server_port,
|
||||
uv_stream_t* server_socket);
|
||||
void Send(const std::string& message);
|
||||
void Close();
|
||||
|
||||
int id() const { return id_; }
|
||||
bool IsForTarget(const std::string& target_id) const {
|
||||
return target_id_ == target_id;
|
||||
}
|
||||
static int ServerPortForClient(InspectorSocket* client) {
|
||||
return From(client)->server_port_;
|
||||
}
|
||||
|
||||
private:
|
||||
SocketSession(InspectorSocketServer* server, int server_port);
|
||||
static SocketSession* From(InspectorSocket* socket) {
|
||||
return node::ContainerOf(&SocketSession::socket_, socket);
|
||||
}
|
||||
|
||||
enum class State { kHttp, kWebSocket, kClosing, kEOF, kDeclined };
|
||||
static bool HandshakeCallback(InspectorSocket* socket,
|
||||
enum inspector_handshake_event state,
|
||||
const std::string& path);
|
||||
static void ReadCallback(uv_stream_t* stream, ssize_t read,
|
||||
const uv_buf_t* buf);
|
||||
static void CloseCallback(InspectorSocket* socket, int code);
|
||||
|
||||
void FrontendConnected();
|
||||
void SetDeclined() { state_ = State::kDeclined; }
|
||||
void SetTargetId(const std::string& target_id) {
|
||||
CHECK(target_id_.empty());
|
||||
target_id_ = target_id;
|
||||
}
|
||||
|
||||
const int id_;
|
||||
InspectorSocket socket_;
|
||||
InspectorSocketServer* server_;
|
||||
std::string target_id_;
|
||||
State state_;
|
||||
const int server_port_;
|
||||
};
|
||||
|
||||
class ServerSocket {
|
||||
public:
|
||||
static int Listen(InspectorSocketServer* inspector_server,
|
||||
sockaddr* addr, uv_loop_t* loop);
|
||||
void Close() {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_),
|
||||
SocketClosedCallback);
|
||||
}
|
||||
int port() const { return port_; }
|
||||
|
||||
private:
|
||||
explicit ServerSocket(InspectorSocketServer* server)
|
||||
: tcp_socket_(uv_tcp_t()), server_(server), port_(-1) {}
|
||||
template<typename UvHandle>
|
||||
static ServerSocket* FromTcpSocket(UvHandle* socket) {
|
||||
return node::ContainerOf(&ServerSocket::tcp_socket_,
|
||||
reinterpret_cast<uv_tcp_t*>(socket));
|
||||
}
|
||||
|
||||
static void SocketConnectedCallback(uv_stream_t* tcp_socket, int status);
|
||||
static void SocketClosedCallback(uv_handle_t* tcp_socket);
|
||||
static void FreeOnCloseCallback(uv_handle_t* tcp_socket_) {
|
||||
delete FromTcpSocket(tcp_socket_);
|
||||
}
|
||||
int DetectPort();
|
||||
|
||||
uv_tcp_t tcp_socket_;
|
||||
InspectorSocketServer* server_;
|
||||
int port_;
|
||||
};
|
||||
|
||||
InspectorSocketServer::InspectorSocketServer(SocketServerDelegate* delegate,
|
||||
uv_loop_t* loop,
|
||||
const std::string& host,
|
||||
int port,
|
||||
FILE* out) : loop_(loop),
|
||||
delegate_(delegate),
|
||||
host_(host),
|
||||
port_(port),
|
||||
closer_(nullptr),
|
||||
next_session_id_(0),
|
||||
out_(out) {
|
||||
state_ = ServerState::kNew;
|
||||
}
|
||||
|
||||
bool InspectorSocketServer::SessionStarted(SocketSession* session,
|
||||
const std::string& id) {
|
||||
if (TargetExists(id) && delegate_->StartSession(session->id(), id)) {
|
||||
connected_sessions_[session->id()] = session;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorSocketServer::SessionTerminated(SocketSession* session) {
|
||||
int id = session->id();
|
||||
if (connected_sessions_.erase(id) != 0) {
|
||||
delegate_->EndSession(id);
|
||||
if (connected_sessions_.empty()) {
|
||||
if (state_ == ServerState::kRunning && !server_sockets_.empty()) {
|
||||
PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
|
||||
delegate_->GetTargetIds(), out_);
|
||||
}
|
||||
if (state_ == ServerState::kStopped) {
|
||||
delegate_->ServerDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
delete session;
|
||||
}
|
||||
|
||||
bool InspectorSocketServer::HandleGetRequest(InspectorSocket* socket,
|
||||
const std::string& path) {
|
||||
const char* command = MatchPathSegment(path.c_str(), "/json");
|
||||
if (command == nullptr)
|
||||
return false;
|
||||
|
||||
if (MatchPathSegment(command, "list") || command[0] == '\0') {
|
||||
SendListResponse(socket);
|
||||
return true;
|
||||
} else if (MatchPathSegment(command, "protocol")) {
|
||||
SendProtocolJson(socket);
|
||||
return true;
|
||||
} else if (MatchPathSegment(command, "version")) {
|
||||
SendVersionResponse(socket);
|
||||
return true;
|
||||
} else if (const char* target_id = MatchPathSegment(command, "activate")) {
|
||||
if (TargetExists(target_id)) {
|
||||
SendHttpResponse(socket, "Target activated");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InspectorSocketServer::SendListResponse(InspectorSocket* socket) {
|
||||
std::vector<std::map<std::string, std::string>> response;
|
||||
for (const std::string& id : delegate_->GetTargetIds()) {
|
||||
response.push_back(std::map<std::string, std::string>());
|
||||
std::map<std::string, std::string>& target_map = response.back();
|
||||
target_map["description"] = "node.js instance";
|
||||
target_map["faviconUrl"] = "https://nodejs.org/static/favicon.ico";
|
||||
target_map["id"] = id;
|
||||
target_map["title"] = delegate_->GetTargetTitle(id);
|
||||
Escape(&target_map["title"]);
|
||||
target_map["type"] = "node";
|
||||
// This attribute value is a "best effort" URL that is passed as a JSON
|
||||
// string. It is not guaranteed to resolve to a valid resource.
|
||||
target_map["url"] = delegate_->GetTargetUrl(id);
|
||||
Escape(&target_map["url"]);
|
||||
|
||||
bool connected = false;
|
||||
for (const auto& session : connected_sessions_) {
|
||||
if (session.second->IsForTarget(id)) {
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!connected) {
|
||||
std::string host;
|
||||
int port = SocketSession::ServerPortForClient(socket);
|
||||
GetSocketHost(&socket->tcp, &host);
|
||||
std::ostringstream frontend_url;
|
||||
frontend_url << "devtools://devtools/bundled";
|
||||
frontend_url << "/js_app.html?experiments=true&v8only=true&ws=";
|
||||
frontend_url << FormatWsAddress(host, port, id, false);
|
||||
target_map["devtoolsFrontendUrl"] += frontend_url.str();
|
||||
target_map["webSocketDebuggerUrl"] =
|
||||
FormatWsAddress(host, port, id, true);
|
||||
}
|
||||
}
|
||||
SendHttpResponse(socket, MapsToString(response));
|
||||
}
|
||||
|
||||
bool InspectorSocketServer::Start() {
|
||||
CHECK_EQ(state_, ServerState::kNew);
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
uv_getaddrinfo_t req;
|
||||
const std::string port_string = to_string(port_);
|
||||
int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(),
|
||||
port_string.c_str(), &hints);
|
||||
if (err < 0) {
|
||||
SE_LOGE("Unable to resolve \"%s\": %s\n", host_.c_str(),
|
||||
uv_strerror(err));
|
||||
return false;
|
||||
}
|
||||
for (addrinfo* address = req.addrinfo; address != nullptr;
|
||||
address = address->ai_next) {
|
||||
err = ServerSocket::Listen(this, address->ai_addr, loop_);
|
||||
}
|
||||
uv_freeaddrinfo(req.addrinfo);
|
||||
|
||||
if (!connected_sessions_.empty()) {
|
||||
return true;
|
||||
}
|
||||
// We only show error if we failed to start server on all addresses. We only
|
||||
// show one error, for the last address.
|
||||
if (server_sockets_.empty()) {
|
||||
SE_LOGE("Starting inspector on %s:%d failed: %s\n",
|
||||
host_.c_str(), port_, uv_strerror(err));
|
||||
if(err == UV_EADDRINUSE) {
|
||||
SE_LOGE("[FATAL ERROR]: Port [:%s] is occupied by other processes, try to kill the previous debug process or change the port number in `jsb_enable_debugger`.\n", port_string.c_str());
|
||||
} else {
|
||||
SE_LOGE("[FATAL ERROR]: Failed to bind port [%s], error code: %d.\n", port_string.c_str(), err);
|
||||
}
|
||||
assert(false);//failed to start socket server for chrome debugger
|
||||
return false;
|
||||
}
|
||||
state_ = ServerState::kRunning;
|
||||
// getaddrinfo sorts the addresses, so the first port is most relevant.
|
||||
PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
|
||||
delegate_->GetTargetIds(), out_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void InspectorSocketServer::Stop(ServerCallback cb) {
|
||||
CHECK_EQ(state_, ServerState::kRunning);
|
||||
if (closer_ == nullptr) {
|
||||
closer_ = new Closer(this);
|
||||
}
|
||||
closer_->AddCallback(cb);
|
||||
closer_->IncreaseExpectedCount();
|
||||
state_ = ServerState::kStopping;
|
||||
for (ServerSocket* server_socket : server_sockets_)
|
||||
server_socket->Close();
|
||||
closer_->NotifyIfDone();
|
||||
}
|
||||
|
||||
void InspectorSocketServer::TerminateConnections() {
|
||||
for (const auto& session : connected_sessions_) {
|
||||
session.second->Close();
|
||||
}
|
||||
}
|
||||
|
||||
bool InspectorSocketServer::TargetExists(const std::string& id) {
|
||||
const std::vector<std::string>& target_ids = delegate_->GetTargetIds();
|
||||
const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
|
||||
return found != target_ids.end();
|
||||
}
|
||||
|
||||
void InspectorSocketServer::Send(int session_id, const std::string& message) {
|
||||
auto session_iterator = connected_sessions_.find(session_id);
|
||||
if (session_iterator != connected_sessions_.end()) {
|
||||
session_iterator->second->Send(message);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorSocketServer::ServerSocketListening(ServerSocket* server_socket) {
|
||||
server_sockets_.push_back(server_socket);
|
||||
}
|
||||
|
||||
void InspectorSocketServer::ServerSocketClosed(ServerSocket* server_socket) {
|
||||
CHECK_EQ(state_, ServerState::kStopping);
|
||||
|
||||
server_sockets_.erase(std::remove(server_sockets_.begin(),
|
||||
server_sockets_.end(), server_socket),
|
||||
server_sockets_.end());
|
||||
if (!server_sockets_.empty())
|
||||
return;
|
||||
|
||||
if (closer_ != nullptr) {
|
||||
closer_->DecreaseExpectedCount();
|
||||
}
|
||||
if (connected_sessions_.empty()) {
|
||||
delegate_->ServerDone();
|
||||
}
|
||||
state_ = ServerState::kStopped;
|
||||
}
|
||||
|
||||
int InspectorSocketServer::Port() const {
|
||||
if (!server_sockets_.empty()) {
|
||||
return server_sockets_[0]->port();
|
||||
}
|
||||
return port_;
|
||||
}
|
||||
|
||||
// InspectorSession tracking
|
||||
SocketSession::SocketSession(InspectorSocketServer* server, int server_port)
|
||||
: id_(server->GenerateSessionId()),
|
||||
server_(server),
|
||||
state_(State::kHttp),
|
||||
server_port_(server_port) { }
|
||||
|
||||
void SocketSession::Close() {
|
||||
CHECK_NE(state_, State::kClosing);
|
||||
state_ = State::kClosing;
|
||||
inspector_close(&socket_, CloseCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
int SocketSession::Accept(InspectorSocketServer* server, int server_port,
|
||||
uv_stream_t* server_socket) {
|
||||
// Memory is freed when the socket closes.
|
||||
SocketSession* session = new SocketSession(server, server_port);
|
||||
int err = inspector_accept(server_socket, &session->socket_,
|
||||
HandshakeCallback);
|
||||
if (err != 0) {
|
||||
delete session;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SocketSession::HandshakeCallback(InspectorSocket* socket,
|
||||
inspector_handshake_event event,
|
||||
const std::string& path) {
|
||||
SocketSession* session = SocketSession::From(socket);
|
||||
InspectorSocketServer* server = session->server_;
|
||||
const std::string& id = path.empty() ? path : path.substr(1);
|
||||
switch (event) {
|
||||
case kInspectorHandshakeHttpGet:
|
||||
return server->HandleGetRequest(socket, path);
|
||||
case kInspectorHandshakeUpgrading:
|
||||
if (server->SessionStarted(session, id)) {
|
||||
session->SetTargetId(id);
|
||||
return true;
|
||||
} else {
|
||||
session->SetDeclined();
|
||||
return false;
|
||||
}
|
||||
case kInspectorHandshakeUpgraded:
|
||||
session->FrontendConnected();
|
||||
return true;
|
||||
case kInspectorHandshakeFailed:
|
||||
server->SessionTerminated(session);
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void SocketSession::CloseCallback(InspectorSocket* socket, int code) {
|
||||
SocketSession* session = SocketSession::From(socket);
|
||||
CHECK_EQ(State::kClosing, session->state_);
|
||||
session->server_->SessionTerminated(session);
|
||||
}
|
||||
|
||||
void SocketSession::FrontendConnected() {
|
||||
CHECK_EQ(State::kHttp, state_);
|
||||
state_ = State::kWebSocket;
|
||||
inspector_read_start(&socket_, OnBufferAlloc, ReadCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
void SocketSession::ReadCallback(uv_stream_t* stream, ssize_t read,
|
||||
const uv_buf_t* buf) {
|
||||
InspectorSocket* socket = inspector_from_stream(stream);
|
||||
SocketSession* session = SocketSession::From(socket);
|
||||
if (read > 0) {
|
||||
session->server_->MessageReceived(session->id_,
|
||||
std::string(buf->base, read));
|
||||
} else {
|
||||
session->Close();
|
||||
}
|
||||
if (buf != nullptr && buf->base != nullptr)
|
||||
delete[] buf->base;
|
||||
}
|
||||
|
||||
void SocketSession::Send(const std::string& message) {
|
||||
inspector_write(&socket_, message.data(), message.length());
|
||||
}
|
||||
|
||||
// ServerSocket implementation
|
||||
int ServerSocket::DetectPort() {
|
||||
sockaddr_storage addr;
|
||||
int len = sizeof(addr);
|
||||
int err = uv_tcp_getsockname(&tcp_socket_,
|
||||
reinterpret_cast<struct sockaddr*>(&addr), &len);
|
||||
if (err != 0)
|
||||
return err;
|
||||
int port;
|
||||
if (addr.ss_family == AF_INET6)
|
||||
port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
|
||||
else
|
||||
port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
|
||||
port_ = ntohs(port);
|
||||
return err;
|
||||
}
|
||||
|
||||
// static
|
||||
int ServerSocket::Listen(InspectorSocketServer* inspector_server,
|
||||
sockaddr* addr, uv_loop_t* loop) {
|
||||
ServerSocket* server_socket = new ServerSocket(inspector_server);
|
||||
uv_tcp_t* server = &server_socket->tcp_socket_;
|
||||
CHECK_EQ(0, uv_tcp_init(loop, server));
|
||||
int err = uv_tcp_bind(server, addr, 0);
|
||||
if (err == 0) {
|
||||
err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 1,
|
||||
ServerSocket::SocketConnectedCallback);
|
||||
}
|
||||
if (err == 0) {
|
||||
err = server_socket->DetectPort();
|
||||
}
|
||||
if (err == 0) {
|
||||
inspector_server->ServerSocketListening(server_socket);
|
||||
} else {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(server), FreeOnCloseCallback);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// static
|
||||
void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
|
||||
int status) {
|
||||
if (status == 0) {
|
||||
ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
|
||||
// Memory is freed when the socket closes.
|
||||
SocketSession::Accept(server_socket->server_, server_socket->port_,
|
||||
tcp_socket);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void ServerSocket::SocketClosedCallback(uv_handle_t* tcp_socket) {
|
||||
ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
|
||||
server_socket->server_->ServerSocketClosed(server_socket);
|
||||
delete server_socket;
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,104 @@
|
||||
#ifndef SRC_INSPECTOR_SOCKET_SERVER_H_
|
||||
#define SRC_INSPECTOR_SOCKET_SERVER_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_agent.h"
|
||||
#include "inspector_socket.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if !HAVE_INSPECTOR
|
||||
#error("This header can only be used when inspector is enabled")
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
class Closer;
|
||||
class SocketSession;
|
||||
class ServerSocket;
|
||||
|
||||
class SocketServerDelegate {
|
||||
public:
|
||||
virtual bool StartSession(int session_id, const std::string& target_id) = 0;
|
||||
virtual void EndSession(int session_id) = 0;
|
||||
virtual void MessageReceived(int session_id, const std::string& message) = 0;
|
||||
virtual std::vector<std::string> GetTargetIds() = 0;
|
||||
virtual std::string GetTargetTitle(const std::string& id) = 0;
|
||||
virtual std::string GetTargetUrl(const std::string& id) = 0;
|
||||
virtual void ServerDone() = 0;
|
||||
};
|
||||
|
||||
// HTTP Server, writes messages requested as TransportActions, and responds
|
||||
// to HTTP requests and WS upgrades.
|
||||
|
||||
|
||||
|
||||
class InspectorSocketServer {
|
||||
public:
|
||||
using ServerCallback = void (*)(InspectorSocketServer*);
|
||||
InspectorSocketServer(SocketServerDelegate* delegate,
|
||||
uv_loop_t* loop,
|
||||
const std::string& host,
|
||||
int port,
|
||||
FILE* out = stderr);
|
||||
// Start listening on host/port
|
||||
bool Start();
|
||||
|
||||
// Called by the TransportAction sent with InspectorIo::Write():
|
||||
// kKill and kStop
|
||||
void Stop(ServerCallback callback);
|
||||
// kSendMessage
|
||||
void Send(int session_id, const std::string& message);
|
||||
// kKill
|
||||
void TerminateConnections();
|
||||
|
||||
int Port() const;
|
||||
|
||||
// Server socket lifecycle. There may be multiple sockets
|
||||
void ServerSocketListening(ServerSocket* server_socket);
|
||||
void ServerSocketClosed(ServerSocket* server_socket);
|
||||
|
||||
// Session connection lifecycle
|
||||
bool HandleGetRequest(InspectorSocket* socket, const std::string& path);
|
||||
bool SessionStarted(SocketSession* session, const std::string& id);
|
||||
void SessionTerminated(SocketSession* session);
|
||||
void MessageReceived(int session_id, const std::string& message) {
|
||||
delegate_->MessageReceived(session_id, message);
|
||||
}
|
||||
|
||||
int GenerateSessionId() {
|
||||
return next_session_id_++;
|
||||
}
|
||||
|
||||
private:
|
||||
void SendListResponse(InspectorSocket* socket);
|
||||
bool TargetExists(const std::string& id);
|
||||
|
||||
enum class ServerState {kNew, kRunning, kStopping, kStopped};
|
||||
uv_loop_t* loop_;
|
||||
SocketServerDelegate* const delegate_;
|
||||
const std::string host_;
|
||||
int port_;
|
||||
std::string path_;
|
||||
std::vector<ServerSocket*> server_sockets_;
|
||||
Closer* closer_;
|
||||
std::map<int, SocketSession*> connected_sessions_;
|
||||
int next_session_id_;
|
||||
FILE* out_;
|
||||
ServerState state_;
|
||||
|
||||
friend class Closer;
|
||||
};
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_INSPECTOR_SOCKET_SERVER_H_
|
||||
1138
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/debugger/node.cc
Normal file
1138
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/debugger/node.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef BUILDING_NODE_EXTENSION
|
||||
# define NODE_EXTERN __declspec(dllexport)
|
||||
# else
|
||||
# define NODE_EXTERN __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define NODE_EXTERN /* nothing */
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef NODE_STRINGIFY
|
||||
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
|
||||
#define NODE_STRINGIFY_HELPER(n) #n
|
||||
#endif
|
||||
|
||||
// The arraysize(arr) macro returns the # of elements in an array arr.
|
||||
// The expression is a compile-time constant, and therefore can be
|
||||
// used in defining new arrays, for example. If you use arraysize on
|
||||
// a pointer by mistake, you will get a compile-time error.
|
||||
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
|
||||
|
||||
|
||||
// This template function declaration is used in defining arraysize.
|
||||
// Note that the function doesn't need an implementation, as we only
|
||||
// use its type.
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(T (&array)[N]))[N];
|
||||
|
||||
|
||||
#if !V8_CC_MSVC
|
||||
// That gcc wants both of these prototypes seems mysterious. VC, for
|
||||
// its part, can't decide which to use (another mystery). Matching of
|
||||
// template overloads: the final frontier.
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(const T (&array)[N]))[N];
|
||||
#endif
|
||||
|
||||
#ifdef __POSIX__
|
||||
void RegisterSignalHandler(int signal,
|
||||
void (*handler)(int signal),
|
||||
bool reset_handler = false);
|
||||
#endif // __POSIX__
|
||||
|
||||
namespace node {
|
||||
|
||||
|
||||
|
||||
NODE_EXTERN v8::Local<v8::Value> ErrnoException(v8::Isolate* isolate,
|
||||
int errorno,
|
||||
const char* syscall = NULL,
|
||||
const char* message = NULL,
|
||||
const char* path = NULL);
|
||||
NODE_EXTERN v8::Local<v8::Value> UVException(v8::Isolate* isolate,
|
||||
int errorno,
|
||||
const char* syscall = NULL,
|
||||
const char* message = NULL,
|
||||
const char* path = NULL);
|
||||
NODE_EXTERN v8::Local<v8::Value> UVException(v8::Isolate* isolate,
|
||||
int errorno,
|
||||
const char* syscall,
|
||||
const char* message,
|
||||
const char* path,
|
||||
const char* dest);
|
||||
|
||||
|
||||
typedef double async_id;
|
||||
struct async_context {
|
||||
::node::async_id async_id;
|
||||
::node::async_id trigger_async_id;
|
||||
};
|
||||
|
||||
/* An API specific to emit before/after callbacks is unnecessary because
|
||||
* MakeCallback will automatically call them for you.
|
||||
*
|
||||
* These methods may create handles on their own, so run them inside a
|
||||
* HandleScope.
|
||||
*
|
||||
* `asyncId` and `triggerAsyncId` should correspond to the values returned by
|
||||
* `EmitAsyncInit()` and `AsyncHooksGetTriggerAsyncId()`, respectively, when the
|
||||
* invoking resource was created. If these values are unknown, 0 can be passed.
|
||||
* */
|
||||
|
||||
v8::MaybeLocal<v8::Value> MakeCallback(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
v8::Local<v8::Function> callback,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv,
|
||||
async_context asyncContext);
|
||||
|
||||
v8::MaybeLocal<v8::Value> MakeCallback(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
const char* method,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv,
|
||||
async_context asyncContext);
|
||||
|
||||
v8::MaybeLocal<v8::Value> MakeCallback(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
v8::Local<v8::String> symbol,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv,
|
||||
async_context asyncContext);
|
||||
|
||||
/*
|
||||
* These methods need to be called in a HandleScope.
|
||||
*
|
||||
* It is preferred that you use the `MakeCallback` overloads taking
|
||||
* `async_id` arguments.
|
||||
*/
|
||||
|
||||
v8::Local<v8::Value> MakeCallback(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
const char* method,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
v8::Local<v8::Value> MakeCallback(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
v8::Local<v8::String> symbol,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
v8::Local<v8::Value> MakeCallback(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
v8::Local<v8::Function> callback,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
|
||||
class IsolateData;
|
||||
class Environment;
|
||||
|
||||
NODE_EXTERN IsolateData* CreateIsolateData(v8::Isolate* isolate,
|
||||
struct uv_loop_s* loop);
|
||||
NODE_EXTERN void FreeIsolateData(IsolateData* isolate_data);
|
||||
|
||||
NODE_EXTERN Environment* CreateEnvironment(IsolateData* isolate_data,
|
||||
v8::Local<v8::Context> context,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv);
|
||||
NODE_EXTERN void FreeEnvironment(Environment* env);
|
||||
|
||||
void SetupProcessObject(Environment* env,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv);
|
||||
|
||||
} // namespace node {
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,141 @@
|
||||
#include "node_debug_options.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
namespace {
|
||||
const int default_inspector_port = 9229;
|
||||
|
||||
inline std::string remove_brackets(const std::string& host) {
|
||||
if (!host.empty() && host.front() == '[' && host.back() == ']')
|
||||
return host.substr(1, host.size() - 2);
|
||||
else
|
||||
return host;
|
||||
}
|
||||
|
||||
int parse_and_validate_port(const std::string& port) {
|
||||
char* endptr;
|
||||
errno = 0;
|
||||
const long result = strtol(port.c_str(), &endptr, 10); // NOLINT(runtime/int)
|
||||
if (errno != 0 || *endptr != '\0'||
|
||||
(result != 0 && result < 1024) || result > 65535) {
|
||||
SE_LOGE("Debug port must be 0 or in range 1024 to 65535.\n");
|
||||
exit(12);
|
||||
}
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
std::pair<std::string, int> split_host_port(const std::string& arg) {
|
||||
// remove_brackets only works if no port is specified
|
||||
// so if it has an effect only an IPv6 address was specified
|
||||
std::string host = remove_brackets(arg);
|
||||
if (host.length() < arg.length())
|
||||
return {host, -1};
|
||||
|
||||
size_t colon = arg.rfind(':');
|
||||
if (colon == std::string::npos) {
|
||||
// Either a port number or a host name. Assume that
|
||||
// if it's not all decimal digits, it's a host name.
|
||||
for (char c : arg) {
|
||||
if (c < '0' || c > '9') {
|
||||
return {arg, -1};
|
||||
}
|
||||
}
|
||||
return {"", parse_and_validate_port(arg)};
|
||||
}
|
||||
// host and port found
|
||||
return std::make_pair(remove_brackets(arg.substr(0, colon)),
|
||||
parse_and_validate_port(arg.substr(colon + 1)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DebugOptions::DebugOptions() :
|
||||
inspector_enabled_(false),
|
||||
deprecated_debug_(false),
|
||||
break_first_line_(false),
|
||||
host_name_("127.0.0.1"), port_(-1) { }
|
||||
|
||||
bool DebugOptions::ParseOption(const char* argv0, const std::string& option) {
|
||||
bool has_argument = false;
|
||||
std::string option_name;
|
||||
std::string argument;
|
||||
|
||||
auto pos = option.find("=");
|
||||
if (pos == std::string::npos) {
|
||||
option_name = option;
|
||||
} else {
|
||||
option_name = option.substr(0, pos);
|
||||
argument = option.substr(pos + 1);
|
||||
|
||||
if (argument.length() > 0)
|
||||
has_argument = true;
|
||||
else
|
||||
argument.clear();
|
||||
}
|
||||
|
||||
// Note that --debug-port and --debug-brk in conjunction with --inspect
|
||||
// work but are undocumented.
|
||||
// --debug is no longer valid.
|
||||
// Ref: https://github.com/nodejs/node/issues/12630
|
||||
// Ref: https://github.com/nodejs/node/pull/12949
|
||||
if (option_name == "--inspect") {
|
||||
inspector_enabled_ = true;
|
||||
} else if (option_name == "--debug") {
|
||||
deprecated_debug_ = true;
|
||||
} else if (option_name == "--inspect-brk") {
|
||||
inspector_enabled_ = true;
|
||||
break_first_line_ = true;
|
||||
} else if (option_name == "--debug-brk") {
|
||||
break_first_line_ = true;
|
||||
deprecated_debug_ = true;
|
||||
} else if (option_name == "--debug-port" ||
|
||||
option_name == "--inspect-port") {
|
||||
if (!has_argument) {
|
||||
SE_LOGE("%s: %s requires an argument\n",
|
||||
argv0, option.c_str());
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !HAVE_INSPECTOR
|
||||
if (inspector_enabled_) {
|
||||
SE_LOGE("Inspector support is not available with this Node.js build\n");
|
||||
}
|
||||
inspector_enabled_ = false;
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// argument can be specified for *any* option to specify host:port
|
||||
if (has_argument) {
|
||||
std::pair<std::string, int> host_port = split_host_port(argument);
|
||||
if (!host_port.first.empty()) {
|
||||
host_name_ = host_port.first;
|
||||
}
|
||||
if (host_port.second >= 0) {
|
||||
port_ = host_port.second;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int DebugOptions::port() const {
|
||||
int port = port_;
|
||||
if (port < 0) {
|
||||
port = default_inspector_port;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef SRC_NODE_DEBUG_OPTIONS_H_
|
||||
#define SRC_NODE_DEBUG_OPTIONS_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include <string>
|
||||
|
||||
// Forward declaration to break recursive dependency chain with src/env.h.
|
||||
namespace node {
|
||||
|
||||
class DebugOptions {
|
||||
public:
|
||||
DebugOptions();
|
||||
bool ParseOption(const char* argv0, const std::string& option);
|
||||
void set_inspector_enabled(bool enabled) { inspector_enabled_ = enabled; }
|
||||
bool inspector_enabled() const { return inspector_enabled_; }
|
||||
bool deprecated_invocation() const {
|
||||
return deprecated_debug_ &&
|
||||
inspector_enabled_ &&
|
||||
break_first_line_;
|
||||
}
|
||||
bool invalid_invocation() const {
|
||||
return deprecated_debug_ && !inspector_enabled_;
|
||||
}
|
||||
void set_wait_for_connect(bool wait) { break_first_line_ = wait; }
|
||||
bool wait_for_connect() const { return break_first_line_; }
|
||||
std::string host_name() const { return host_name_; }
|
||||
void set_host_name(std::string host_name) { host_name_ = host_name; }
|
||||
int port() const;
|
||||
void set_port(int port) { port_ = port; }
|
||||
|
||||
private:
|
||||
bool inspector_enabled_;
|
||||
bool deprecated_debug_;
|
||||
bool break_first_line_;
|
||||
std::string host_name_;
|
||||
int port_;
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_NODE_DEBUG_OPTIONS_H_
|
||||
@@ -0,0 +1,192 @@
|
||||
#ifndef SRC_NODE_MUTEX_H_
|
||||
#define SRC_NODE_MUTEX_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "util.h"
|
||||
#include "uv.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
template <typename Traits> class ConditionVariableBase;
|
||||
template <typename Traits> class MutexBase;
|
||||
struct LibuvMutexTraits;
|
||||
|
||||
using ConditionVariable = ConditionVariableBase<LibuvMutexTraits>;
|
||||
using Mutex = MutexBase<LibuvMutexTraits>;
|
||||
|
||||
template <typename Traits>
|
||||
class MutexBase {
|
||||
public:
|
||||
inline MutexBase();
|
||||
inline ~MutexBase();
|
||||
inline void Lock();
|
||||
inline void Unlock();
|
||||
|
||||
class ScopedLock;
|
||||
class ScopedUnlock;
|
||||
|
||||
class ScopedLock {
|
||||
public:
|
||||
inline explicit ScopedLock(const MutexBase& mutex);
|
||||
inline explicit ScopedLock(const ScopedUnlock& scoped_unlock);
|
||||
inline ~ScopedLock();
|
||||
|
||||
private:
|
||||
template <typename> friend class ConditionVariableBase;
|
||||
friend class ScopedUnlock;
|
||||
const MutexBase& mutex_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ScopedLock);
|
||||
};
|
||||
|
||||
class ScopedUnlock {
|
||||
public:
|
||||
inline explicit ScopedUnlock(const ScopedLock& scoped_lock);
|
||||
inline ~ScopedUnlock();
|
||||
|
||||
private:
|
||||
friend class ScopedLock;
|
||||
const MutexBase& mutex_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ScopedUnlock);
|
||||
};
|
||||
|
||||
private:
|
||||
template <typename> friend class ConditionVariableBase;
|
||||
mutable typename Traits::MutexT mutex_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(MutexBase);
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
class ConditionVariableBase {
|
||||
public:
|
||||
using ScopedLock = typename MutexBase<Traits>::ScopedLock;
|
||||
|
||||
inline ConditionVariableBase();
|
||||
inline ~ConditionVariableBase();
|
||||
inline void Broadcast(const ScopedLock&);
|
||||
inline void Signal(const ScopedLock&);
|
||||
inline void Wait(const ScopedLock& scoped_lock);
|
||||
|
||||
private:
|
||||
typename Traits::CondT cond_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ConditionVariableBase);
|
||||
};
|
||||
|
||||
struct LibuvMutexTraits {
|
||||
using CondT = uv_cond_t;
|
||||
using MutexT = uv_mutex_t;
|
||||
|
||||
static inline int cond_init(CondT* cond) {
|
||||
return uv_cond_init(cond);
|
||||
}
|
||||
|
||||
static inline int mutex_init(MutexT* mutex) {
|
||||
return uv_mutex_init(mutex);
|
||||
}
|
||||
|
||||
static inline void cond_broadcast(CondT* cond) {
|
||||
uv_cond_broadcast(cond);
|
||||
}
|
||||
|
||||
static inline void cond_destroy(CondT* cond) {
|
||||
uv_cond_destroy(cond);
|
||||
}
|
||||
|
||||
static inline void cond_signal(CondT* cond) {
|
||||
uv_cond_signal(cond);
|
||||
}
|
||||
|
||||
static inline void cond_wait(CondT* cond, MutexT* mutex) {
|
||||
uv_cond_wait(cond, mutex);
|
||||
}
|
||||
|
||||
static inline void mutex_destroy(MutexT* mutex) {
|
||||
uv_mutex_destroy(mutex);
|
||||
}
|
||||
|
||||
static inline void mutex_lock(MutexT* mutex) {
|
||||
uv_mutex_lock(mutex);
|
||||
}
|
||||
|
||||
static inline void mutex_unlock(MutexT* mutex) {
|
||||
uv_mutex_unlock(mutex);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
ConditionVariableBase<Traits>::ConditionVariableBase() {
|
||||
CHECK_EQ(0, Traits::cond_init(&cond_));
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
ConditionVariableBase<Traits>::~ConditionVariableBase() {
|
||||
Traits::cond_destroy(&cond_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void ConditionVariableBase<Traits>::Broadcast(const ScopedLock&) {
|
||||
Traits::cond_broadcast(&cond_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void ConditionVariableBase<Traits>::Signal(const ScopedLock&) {
|
||||
Traits::cond_signal(&cond_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void ConditionVariableBase<Traits>::Wait(const ScopedLock& scoped_lock) {
|
||||
Traits::cond_wait(&cond_, &scoped_lock.mutex_.mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::MutexBase() {
|
||||
CHECK_EQ(0, Traits::mutex_init(&mutex_));
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::~MutexBase() {
|
||||
Traits::mutex_destroy(&mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void MutexBase<Traits>::Lock() {
|
||||
Traits::mutex_lock(&mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void MutexBase<Traits>::Unlock() {
|
||||
Traits::mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedLock::ScopedLock(const MutexBase& mutex)
|
||||
: mutex_(mutex) {
|
||||
Traits::mutex_lock(&mutex_.mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedLock::ScopedLock(const ScopedUnlock& scoped_unlock)
|
||||
: MutexBase(scoped_unlock.mutex_) {}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedLock::~ScopedLock() {
|
||||
Traits::mutex_unlock(&mutex_.mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedUnlock::ScopedUnlock(const ScopedLock& scoped_lock)
|
||||
: mutex_(scoped_lock.mutex_) {
|
||||
Traits::mutex_unlock(&mutex_.mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedUnlock::~ScopedUnlock() {
|
||||
Traits::mutex_lock(&mutex_.mutex_);
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_NODE_MUTEX_H_
|
||||
@@ -0,0 +1,428 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#ifndef SRC_UTIL_INL_H_
|
||||
#define SRC_UTIL_INL_H_
|
||||
|
||||
#ifndef NODE_UTIL_H_INCLUDE
|
||||
#error "util-inl.h could only be included in util.h"
|
||||
#endif
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "util.h"
|
||||
#include <cstring>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#define BSWAP_2(x) _byteswap_ushort(x)
|
||||
#define BSWAP_4(x) _byteswap_ulong(x)
|
||||
#define BSWAP_8(x) _byteswap_uint64(x)
|
||||
#else
|
||||
#define BSWAP_2(x) ((x) << 8) | ((x) >> 8)
|
||||
#define BSWAP_4(x) \
|
||||
(((x) & 0xFF) << 24) | \
|
||||
(((x) & 0xFF00) << 8) | \
|
||||
(((x) >> 8) & 0xFF00) | \
|
||||
(((x) >> 24) & 0xFF)
|
||||
#define BSWAP_8(x) \
|
||||
(((x) & 0xFF00000000000000ull) >> 56) | \
|
||||
(((x) & 0x00FF000000000000ull) >> 40) | \
|
||||
(((x) & 0x0000FF0000000000ull) >> 24) | \
|
||||
(((x) & 0x000000FF00000000ull) >> 8) | \
|
||||
(((x) & 0x00000000FF000000ull) << 8) | \
|
||||
(((x) & 0x0000000000FF0000ull) << 24) | \
|
||||
(((x) & 0x000000000000FF00ull) << 40) | \
|
||||
(((x) & 0x00000000000000FFull) << 56)
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
|
||||
template <typename T>
|
||||
ListNode<T>::ListNode() : prev_(this), next_(this) {}
|
||||
|
||||
template <typename T>
|
||||
ListNode<T>::~ListNode() {
|
||||
Remove();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ListNode<T>::Remove() {
|
||||
prev_->next_ = next_;
|
||||
next_->prev_ = prev_;
|
||||
prev_ = this;
|
||||
next_ = this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool ListNode<T>::IsEmpty() const {
|
||||
return prev_ == this;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
ListHead<T, M>::Iterator::Iterator(ListNode<T>* node) : node_(node) {}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
T* ListHead<T, M>::Iterator::operator*() const {
|
||||
return ContainerOf(M, node_);
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
const typename ListHead<T, M>::Iterator&
|
||||
ListHead<T, M>::Iterator::operator++() {
|
||||
node_ = node_->next_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
bool ListHead<T, M>::Iterator::operator!=(const Iterator& that) const {
|
||||
return node_ != that.node_;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
ListHead<T, M>::~ListHead() {
|
||||
while (IsEmpty() == false)
|
||||
head_.next_->Remove();
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
void ListHead<T, M>::MoveBack(ListHead* that) {
|
||||
if (IsEmpty())
|
||||
return;
|
||||
ListNode<T>* to = &that->head_;
|
||||
head_.next_->prev_ = to->prev_;
|
||||
to->prev_->next_ = head_.next_;
|
||||
head_.prev_->next_ = to;
|
||||
to->prev_ = head_.prev_;
|
||||
head_.prev_ = &head_;
|
||||
head_.next_ = &head_;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
void ListHead<T, M>::PushBack(T* element) {
|
||||
ListNode<T>* that = &(element->*M);
|
||||
head_.prev_->next_ = that;
|
||||
that->prev_ = head_.prev_;
|
||||
that->next_ = &head_;
|
||||
head_.prev_ = that;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
void ListHead<T, M>::PushFront(T* element) {
|
||||
ListNode<T>* that = &(element->*M);
|
||||
head_.next_->prev_ = that;
|
||||
that->prev_ = &head_;
|
||||
that->next_ = head_.next_;
|
||||
head_.next_ = that;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
bool ListHead<T, M>::IsEmpty() const {
|
||||
return head_.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
T* ListHead<T, M>::PopFront() {
|
||||
if (IsEmpty())
|
||||
return nullptr;
|
||||
ListNode<T>* node = head_.next_;
|
||||
node->Remove();
|
||||
return ContainerOf(M, node);
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
typename ListHead<T, M>::Iterator ListHead<T, M>::begin() const {
|
||||
return Iterator(head_.next_);
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
typename ListHead<T, M>::Iterator ListHead<T, M>::end() const {
|
||||
return Iterator(const_cast<ListNode<T>*>(&head_));
|
||||
}
|
||||
|
||||
template <typename Inner, typename Outer>
|
||||
ContainerOfHelper<Inner, Outer>::ContainerOfHelper(Inner Outer::*field,
|
||||
Inner* pointer)
|
||||
: pointer_(reinterpret_cast<Outer*>(
|
||||
reinterpret_cast<uintptr_t>(pointer) -
|
||||
reinterpret_cast<uintptr_t>(&(static_cast<Outer*>(0)->*field)))) {
|
||||
}
|
||||
|
||||
template <typename Inner, typename Outer>
|
||||
template <typename TypeName>
|
||||
ContainerOfHelper<Inner, Outer>::operator TypeName*() const {
|
||||
return static_cast<TypeName*>(pointer_);
|
||||
}
|
||||
|
||||
template <typename Inner, typename Outer>
|
||||
inline ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::*field,
|
||||
Inner* pointer) {
|
||||
return ContainerOfHelper<Inner, Outer>(field, pointer);
|
||||
}
|
||||
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> PersistentToLocal(
|
||||
v8::Isolate* isolate,
|
||||
const v8::Persistent<TypeName>& persistent) {
|
||||
if (persistent.IsWeak()) {
|
||||
return WeakPersistentToLocal(isolate, persistent);
|
||||
} else {
|
||||
return StrongPersistentToLocal(persistent);
|
||||
}
|
||||
}
|
||||
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> StrongPersistentToLocal(
|
||||
const v8::Persistent<TypeName>& persistent) {
|
||||
return *reinterpret_cast<v8::Local<TypeName>*>(
|
||||
const_cast<v8::Persistent<TypeName>*>(&persistent));
|
||||
}
|
||||
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> WeakPersistentToLocal(
|
||||
v8::Isolate* isolate,
|
||||
const v8::Persistent<TypeName>& persistent) {
|
||||
return v8::Local<TypeName>::New(isolate, persistent);
|
||||
}
|
||||
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const char* data,
|
||||
int length) {
|
||||
return v8::String::NewFromOneByte(isolate,
|
||||
reinterpret_cast<const uint8_t*>(data),
|
||||
v8::NewStringType::kNormal,
|
||||
length).ToLocalChecked();
|
||||
}
|
||||
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const signed char* data,
|
||||
int length) {
|
||||
return v8::String::NewFromOneByte(isolate,
|
||||
reinterpret_cast<const uint8_t*>(data),
|
||||
v8::NewStringType::kNormal,
|
||||
length).ToLocalChecked();
|
||||
}
|
||||
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const unsigned char* data,
|
||||
int length) {
|
||||
return v8::String::NewFromOneByte(isolate,
|
||||
reinterpret_cast<const uint8_t*>(data),
|
||||
v8::NewStringType::kNormal,
|
||||
length).ToLocalChecked();
|
||||
}
|
||||
|
||||
template <typename TypeName>
|
||||
void Wrap(v8::Local<v8::Object> object, TypeName* pointer) {
|
||||
CHECK_EQ(false, object.IsEmpty());
|
||||
CHECK_GT(object->InternalFieldCount(), 0);
|
||||
object->SetAlignedPointerInInternalField(0, pointer);
|
||||
}
|
||||
|
||||
void ClearWrap(v8::Local<v8::Object> object) {
|
||||
Wrap<void>(object, nullptr);
|
||||
}
|
||||
|
||||
template <typename TypeName>
|
||||
TypeName* Unwrap(v8::Local<v8::Object> object) {
|
||||
CHECK_EQ(false, object.IsEmpty());
|
||||
CHECK_GT(object->InternalFieldCount(), 0);
|
||||
void* pointer = object->GetAlignedPointerFromInternalField(0);
|
||||
return static_cast<TypeName*>(pointer);
|
||||
}
|
||||
|
||||
void SwapBytes16(char* data, size_t nbytes) {
|
||||
CHECK_EQ(nbytes % 2, 0);
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint16_t);
|
||||
if (align == 0) {
|
||||
// MSVC has no strict aliasing, and is able to highly optimize this case.
|
||||
uint16_t* data16 = reinterpret_cast<uint16_t*>(data);
|
||||
size_t len16 = nbytes / sizeof(*data16);
|
||||
for (size_t i = 0; i < len16; i++) {
|
||||
data16[i] = BSWAP_2(data16[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t temp;
|
||||
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
|
||||
memcpy(&temp, &data[i], sizeof(temp));
|
||||
temp = BSWAP_2(temp);
|
||||
memcpy(&data[i], &temp, sizeof(temp));
|
||||
}
|
||||
}
|
||||
|
||||
void SwapBytes32(char* data, size_t nbytes) {
|
||||
CHECK_EQ(nbytes % 4, 0);
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint32_t);
|
||||
// MSVC has no strict aliasing, and is able to highly optimize this case.
|
||||
if (align == 0) {
|
||||
uint32_t* data32 = reinterpret_cast<uint32_t*>(data);
|
||||
size_t len32 = nbytes / sizeof(*data32);
|
||||
for (size_t i = 0; i < len32; i++) {
|
||||
data32[i] = BSWAP_4(data32[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t temp;
|
||||
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
|
||||
memcpy(&temp, &data[i], sizeof(temp));
|
||||
temp = BSWAP_4(temp);
|
||||
memcpy(&data[i], &temp, sizeof(temp));
|
||||
}
|
||||
}
|
||||
|
||||
void SwapBytes64(char* data, size_t nbytes) {
|
||||
CHECK_EQ(nbytes % 8, 0);
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint64_t);
|
||||
if (align == 0) {
|
||||
// MSVC has no strict aliasing, and is able to highly optimize this case.
|
||||
uint64_t* data64 = reinterpret_cast<uint64_t*>(data);
|
||||
size_t len64 = nbytes / sizeof(*data64);
|
||||
for (size_t i = 0; i < len64; i++) {
|
||||
data64[i] = BSWAP_8(data64[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t temp;
|
||||
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
|
||||
memcpy(&temp, &data[i], sizeof(temp));
|
||||
temp = BSWAP_8(temp);
|
||||
memcpy(&data[i], &temp, sizeof(temp));
|
||||
}
|
||||
}
|
||||
|
||||
char ToLower(char c) {
|
||||
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
bool StringEqualNoCase(const char* a, const char* b) {
|
||||
do {
|
||||
if (*a == '\0')
|
||||
return *b == '\0';
|
||||
if (*b == '\0')
|
||||
return *a == '\0';
|
||||
} while (ToLower(*a++) == ToLower(*b++));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StringEqualNoCaseN(const char* a, const char* b, size_t length) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (ToLower(a[i]) != ToLower(b[i]))
|
||||
return false;
|
||||
if (a[i] == '\0')
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline size_t MultiplyWithOverflowCheck(size_t a, size_t b) {
|
||||
size_t ret = a * b;
|
||||
if (a != 0)
|
||||
CHECK_EQ(b, ret / a);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// These should be used in our code as opposed to the native
|
||||
// versions as they abstract out some platform and or
|
||||
// compiler version specific functionality.
|
||||
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
|
||||
// that the standard allows them to either return a unique pointer or a
|
||||
// nullptr for zero-sized allocation requests. Normalize by always using
|
||||
// a nullptr.
|
||||
template <typename T>
|
||||
T* UncheckedRealloc(T* pointer, size_t n) {
|
||||
size_t full_size = MultiplyWithOverflowCheck(sizeof(T), n);
|
||||
|
||||
if (full_size == 0) {
|
||||
free(pointer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* allocated = realloc(pointer, full_size);
|
||||
|
||||
if (UNLIKELY(allocated == nullptr)) {
|
||||
// Tell V8 that memory is low and retry.
|
||||
LowMemoryNotification();
|
||||
allocated = realloc(pointer, full_size);
|
||||
}
|
||||
|
||||
return static_cast<T*>(allocated);
|
||||
}
|
||||
|
||||
// As per spec realloc behaves like malloc if passed nullptr.
|
||||
template <typename T>
|
||||
inline T* UncheckedMalloc(size_t n) {
|
||||
if (n == 0) n = 1;
|
||||
return UncheckedRealloc<T>(nullptr, n);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* UncheckedCalloc(size_t n) {
|
||||
if (n == 0) n = 1;
|
||||
MultiplyWithOverflowCheck(sizeof(T), n);
|
||||
return static_cast<T*>(calloc(n, sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* Realloc(T* pointer, size_t n) {
|
||||
T* ret = UncheckedRealloc(pointer, n);
|
||||
if (n > 0) CHECK_NE(ret, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* Malloc(size_t n) {
|
||||
T* ret = UncheckedMalloc<T>(n);
|
||||
if (n > 0) CHECK_NE(ret, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* Calloc(size_t n) {
|
||||
T* ret = UncheckedCalloc<T>(n);
|
||||
if (n > 0) CHECK_NE(ret, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Shortcuts for char*.
|
||||
inline char* Malloc(size_t n) { return Malloc<char>(n); }
|
||||
inline char* Calloc(size_t n) { return Calloc<char>(n); }
|
||||
inline char* UncheckedMalloc(size_t n) { return UncheckedMalloc<char>(n); }
|
||||
inline char* UncheckedCalloc(size_t n) { return UncheckedCalloc<char>(n); }
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#endif // SRC_UTIL_INL_H_
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
//cjh #include "string_bytes.h"
|
||||
//#include "node_buffer.h"
|
||||
//#include "node_internals.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace node {
|
||||
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
template <typename T>
|
||||
static void MakeUtf8String(Isolate* isolate,
|
||||
Local<Value> value,
|
||||
T* target) {
|
||||
Local<String> string;
|
||||
if (! value->ToString(isolate->GetCurrentContext()).ToLocal(&string) )
|
||||
return;
|
||||
|
||||
const size_t storage = 3 * string->Length() + 1;
|
||||
target->AllocateSufficientStorage(storage);
|
||||
const int flags =
|
||||
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
|
||||
const int length = string->WriteUtf8(isolate, target->out(), (int)storage, 0, flags);
|
||||
target->SetLengthAndZeroTerminate(length);
|
||||
}
|
||||
|
||||
Utf8Value::Utf8Value(Isolate* isolate, Local<Value> value) {
|
||||
if (value.IsEmpty())
|
||||
return;
|
||||
|
||||
MakeUtf8String(isolate, value, this);
|
||||
}
|
||||
|
||||
|
||||
TwoByteValue::TwoByteValue(Isolate* isolate, Local<Value> value) {
|
||||
if (value.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Local<String> string;
|
||||
if (! value->ToString(isolate->GetCurrentContext()).ToLocal(&string) )
|
||||
if (string.IsEmpty())
|
||||
return;
|
||||
|
||||
// Allocate enough space to include the null terminator
|
||||
const size_t storage = string->Length() + 1;
|
||||
AllocateSufficientStorage(storage);
|
||||
|
||||
const int flags = String::NO_NULL_TERMINATION;
|
||||
const int length = string->Write(isolate, out(), 0, (int)storage, flags);
|
||||
SetLengthAndZeroTerminate(length);
|
||||
}
|
||||
|
||||
BufferValue::BufferValue(Isolate* isolate, Local<Value> value) {
|
||||
// Slightly different take on Utf8Value. If value is a String,
|
||||
// it will return a Utf8 encoded string. If value is a Buffer,
|
||||
// it will copy the data out of the Buffer as is.
|
||||
if (value.IsEmpty()) {
|
||||
// Dereferencing this object will return nullptr.
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (value->IsString()) {
|
||||
MakeUtf8String(isolate, value, this);
|
||||
//cjh } else if (Buffer::HasInstance(value)) {
|
||||
// const size_t len = Buffer::Length(value);
|
||||
// // Leave place for the terminating '\0' byte.
|
||||
// AllocateSufficientStorage(len + 1);
|
||||
// memcpy(out(), Buffer::Data(value), len);
|
||||
// SetLengthAndZeroTerminate(len);
|
||||
} else {
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void LowMemoryNotification() {
|
||||
// if (v8_initialized) {
|
||||
auto isolate = v8::Isolate::GetCurrent();
|
||||
if (isolate != nullptr) {
|
||||
isolate->LowMemoryNotification();
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
void DumpBacktrace(FILE* fp) {
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,446 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#ifndef SRC_UTIL_H_
|
||||
#define SRC_UTIL_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#define NODE_WANT_INTERNALS 1 //cjh added
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <type_traits> // std::remove_reference
|
||||
|
||||
namespace node {
|
||||
|
||||
// These should be used in our code as opposed to the native
|
||||
// versions as they abstract out some platform and or
|
||||
// compiler version specific functionality
|
||||
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
|
||||
// that the standard allows them to either return a unique pointer or a
|
||||
// nullptr for zero-sized allocation requests. Normalize by always using
|
||||
// a nullptr.
|
||||
template <typename T>
|
||||
inline T* UncheckedRealloc(T* pointer, size_t n);
|
||||
template <typename T>
|
||||
inline T* UncheckedMalloc(size_t n);
|
||||
template <typename T>
|
||||
inline T* UncheckedCalloc(size_t n);
|
||||
|
||||
// Same things, but aborts immediately instead of returning nullptr when
|
||||
// no memory is available.
|
||||
template <typename T>
|
||||
inline T* Realloc(T* pointer, size_t n);
|
||||
template <typename T>
|
||||
inline T* Malloc(size_t n);
|
||||
template <typename T>
|
||||
inline T* Calloc(size_t n);
|
||||
|
||||
inline char* Malloc(size_t n);
|
||||
inline char* Calloc(size_t n);
|
||||
inline char* UncheckedMalloc(size_t n);
|
||||
inline char* UncheckedCalloc(size_t n);
|
||||
|
||||
// Used by the allocation functions when allocation fails.
|
||||
// Thin wrapper around v8::Isolate::LowMemoryNotification() that checks
|
||||
// whether V8 is initialized.
|
||||
void LowMemoryNotification();
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define NO_RETURN __attribute__((noreturn))
|
||||
#else
|
||||
#define NO_RETURN
|
||||
#endif
|
||||
|
||||
// The slightly odd function signature for Assert() is to ease
|
||||
// instruction cache pressure in calls from CHECK.
|
||||
NO_RETURN void Abort();
|
||||
NO_RETURN void Assert(const char* const (*args)[4]);
|
||||
void DumpBacktrace(FILE* fp);
|
||||
|
||||
template <typename T> using remove_reference = std::remove_reference<T>;
|
||||
|
||||
#define FIXED_ONE_BYTE_STRING(isolate, string) \
|
||||
(node::OneByteString((isolate), (string), sizeof(string) - 1))
|
||||
|
||||
#define NODE_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
void operator=(const TypeName&) = delete; \
|
||||
void operator=(TypeName&&) = delete; \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
TypeName(TypeName&&) = delete
|
||||
|
||||
// Windows 8+ does not like abort() in Release mode
|
||||
#ifdef _WIN32
|
||||
#define ABORT_NO_BACKTRACE() raise(SIGABRT)
|
||||
#else
|
||||
#define ABORT_NO_BACKTRACE() abort()
|
||||
#endif
|
||||
|
||||
#define ABORT() node::Abort()
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define LIKELY(expr) __builtin_expect(!!(expr), 1)
|
||||
#define UNLIKELY(expr) __builtin_expect(!!(expr), 0)
|
||||
#define PRETTY_FUNCTION_NAME __PRETTY_FUNCTION__
|
||||
#else
|
||||
#define LIKELY(expr) expr
|
||||
#define UNLIKELY(expr) expr
|
||||
#define PRETTY_FUNCTION_NAME ""
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_(x)
|
||||
|
||||
#define CHECK(expr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
static const char* const args[] = { __FILE__, STRINGIFY(__LINE__), \
|
||||
#expr, PRETTY_FUNCTION_NAME }; \
|
||||
node::Assert(&args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_EQ(a, b) CHECK((a) == (b))
|
||||
#define CHECK_GE(a, b) CHECK((a) >= (b))
|
||||
#define CHECK_GT(a, b) CHECK((a) > (b))
|
||||
#define CHECK_LE(a, b) CHECK((a) <= (b))
|
||||
#define CHECK_LT(a, b) CHECK((a) < (b))
|
||||
#define CHECK_NE(a, b) CHECK((a) != (b))
|
||||
|
||||
#define UNREACHABLE() ABORT()
|
||||
|
||||
#define ASSIGN_OR_RETURN_UNWRAP(ptr, obj, ...) \
|
||||
do { \
|
||||
*ptr = \
|
||||
Unwrap<typename node::remove_reference<decltype(**ptr)>::type>(obj); \
|
||||
if (*ptr == nullptr) \
|
||||
return __VA_ARGS__; \
|
||||
} while (0)
|
||||
|
||||
// TAILQ-style intrusive list node.
|
||||
template <typename T>
|
||||
class ListNode;
|
||||
|
||||
// TAILQ-style intrusive list head.
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
class ListHead;
|
||||
|
||||
template <typename T>
|
||||
class ListNode {
|
||||
public:
|
||||
inline ListNode();
|
||||
inline ~ListNode();
|
||||
inline void Remove();
|
||||
inline bool IsEmpty() const;
|
||||
|
||||
private:
|
||||
template <typename U, ListNode<U> (U::*M)> friend class ListHead;
|
||||
ListNode* prev_;
|
||||
ListNode* next_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ListNode);
|
||||
};
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
class ListHead {
|
||||
public:
|
||||
class Iterator {
|
||||
public:
|
||||
inline T* operator*() const;
|
||||
inline const Iterator& operator++();
|
||||
inline bool operator!=(const Iterator& that) const;
|
||||
|
||||
private:
|
||||
friend class ListHead;
|
||||
inline explicit Iterator(ListNode<T>* node);
|
||||
ListNode<T>* node_;
|
||||
};
|
||||
|
||||
inline ListHead() = default;
|
||||
inline ~ListHead();
|
||||
inline void MoveBack(ListHead* that);
|
||||
inline void PushBack(T* element);
|
||||
inline void PushFront(T* element);
|
||||
inline bool IsEmpty() const;
|
||||
inline T* PopFront();
|
||||
inline Iterator begin() const;
|
||||
inline Iterator end() const;
|
||||
|
||||
private:
|
||||
ListNode<T> head_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ListHead);
|
||||
};
|
||||
|
||||
// The helper is for doing safe downcasts from base types to derived types.
|
||||
template <typename Inner, typename Outer>
|
||||
class ContainerOfHelper {
|
||||
public:
|
||||
inline ContainerOfHelper(Inner Outer::*field, Inner* pointer);
|
||||
template <typename TypeName>
|
||||
inline operator TypeName*() const;
|
||||
private:
|
||||
Outer* const pointer_;
|
||||
};
|
||||
|
||||
// Calculate the address of the outer (i.e. embedding) struct from
|
||||
// the interior pointer to a data member.
|
||||
template <typename Inner, typename Outer>
|
||||
inline ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::*field,
|
||||
Inner* pointer);
|
||||
|
||||
// If persistent.IsWeak() == false, then do not call persistent.Reset()
|
||||
// while the returned Local<T> is still in scope, it will destroy the
|
||||
// reference to the object.
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> PersistentToLocal(
|
||||
v8::Isolate* isolate,
|
||||
const v8::Persistent<TypeName>& persistent);
|
||||
|
||||
// Unchecked conversion from a non-weak Persistent<T> to Local<TLocal<T>,
|
||||
// use with care!
|
||||
//
|
||||
// Do not call persistent.Reset() while the returned Local<T> is still in
|
||||
// scope, it will destroy the reference to the object.
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> StrongPersistentToLocal(
|
||||
const v8::Persistent<TypeName>& persistent);
|
||||
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> WeakPersistentToLocal(
|
||||
v8::Isolate* isolate,
|
||||
const v8::Persistent<TypeName>& persistent);
|
||||
|
||||
// Convenience wrapper around v8::String::NewFromOneByte().
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const char* data,
|
||||
int length = -1);
|
||||
|
||||
// For the people that compile with -funsigned-char.
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const signed char* data,
|
||||
int length = -1);
|
||||
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const unsigned char* data,
|
||||
int length = -1);
|
||||
|
||||
inline void Wrap(v8::Local<v8::Object> object, void* pointer);
|
||||
|
||||
inline void ClearWrap(v8::Local<v8::Object> object);
|
||||
|
||||
template <typename TypeName>
|
||||
inline TypeName* Unwrap(v8::Local<v8::Object> object);
|
||||
|
||||
// Swaps bytes in place. nbytes is the number of bytes to swap and must be a
|
||||
// multiple of the word size (checked by function).
|
||||
inline void SwapBytes16(char* data, size_t nbytes);
|
||||
inline void SwapBytes32(char* data, size_t nbytes);
|
||||
inline void SwapBytes64(char* data, size_t nbytes);
|
||||
|
||||
// tolower() is locale-sensitive. Use ToLower() instead.
|
||||
inline char ToLower(char c);
|
||||
|
||||
// strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead.
|
||||
inline bool StringEqualNoCase(const char* a, const char* b);
|
||||
|
||||
// strncasecmp() is locale-sensitive. Use StringEqualNoCaseN() instead.
|
||||
inline bool StringEqualNoCaseN(const char* a, const char* b, size_t length);
|
||||
|
||||
// Allocates an array of member type T. For up to kStackStorageSize items,
|
||||
// the stack is used, otherwise malloc().
|
||||
template <typename T, size_t kStackStorageSize = 1024>
|
||||
class MaybeStackBuffer {
|
||||
public:
|
||||
const T* out() const {
|
||||
return buf_;
|
||||
}
|
||||
|
||||
T* out() {
|
||||
return buf_;
|
||||
}
|
||||
|
||||
// operator* for compatibility with `v8::String::(Utf8)Value`
|
||||
T* operator*() {
|
||||
return buf_;
|
||||
}
|
||||
|
||||
const T* operator*() const {
|
||||
return buf_;
|
||||
}
|
||||
|
||||
T& operator[](size_t index) {
|
||||
CHECK_LT(index, length());
|
||||
return buf_[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const {
|
||||
CHECK_LT(index, length());
|
||||
return buf_[index];
|
||||
}
|
||||
|
||||
size_t length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
// Current maximum capacity of the buffer with which SetLength() can be used
|
||||
// without first calling AllocateSufficientStorage().
|
||||
size_t capacity() const {
|
||||
return IsAllocated() ? capacity_ :
|
||||
IsInvalidated() ? 0 : kStackStorageSize;
|
||||
}
|
||||
|
||||
// Make sure enough space for `storage` entries is available.
|
||||
// This method can be called multiple times throughout the lifetime of the
|
||||
// buffer, but once this has been called Invalidate() cannot be used.
|
||||
// Content of the buffer in the range [0, length()) is preserved.
|
||||
void AllocateSufficientStorage(size_t storage) {
|
||||
CHECK(!IsInvalidated());
|
||||
if (storage > capacity()) {
|
||||
bool was_allocated = IsAllocated();
|
||||
T* allocated_ptr = was_allocated ? buf_ : nullptr;
|
||||
buf_ = Realloc(allocated_ptr, storage);
|
||||
capacity_ = storage;
|
||||
if (!was_allocated && length_ > 0)
|
||||
memcpy(buf_, buf_st_, length_ * sizeof(buf_[0]));
|
||||
}
|
||||
|
||||
length_ = storage;
|
||||
}
|
||||
|
||||
void SetLength(size_t length) {
|
||||
// capacity() returns how much memory is actually available.
|
||||
CHECK_LE(length, capacity());
|
||||
length_ = length;
|
||||
}
|
||||
|
||||
void SetLengthAndZeroTerminate(size_t length) {
|
||||
// capacity() returns how much memory is actually available.
|
||||
CHECK_LE(length + 1, capacity());
|
||||
SetLength(length);
|
||||
|
||||
// T() is 0 for integer types, nullptr for pointers, etc.
|
||||
buf_[length] = T();
|
||||
}
|
||||
|
||||
// Make derefencing this object return nullptr.
|
||||
// This method can be called multiple times throughout the lifetime of the
|
||||
// buffer, but once this has been called AllocateSufficientStorage() cannot
|
||||
// be used.
|
||||
void Invalidate() {
|
||||
CHECK(!IsAllocated());
|
||||
length_ = 0;
|
||||
buf_ = nullptr;
|
||||
}
|
||||
|
||||
// If the buffer is stored in the heap rather than on the stack.
|
||||
bool IsAllocated() const {
|
||||
return !IsInvalidated() && buf_ != buf_st_;
|
||||
}
|
||||
|
||||
// If Invalidate() has been called.
|
||||
bool IsInvalidated() const {
|
||||
return buf_ == nullptr;
|
||||
}
|
||||
|
||||
// Release ownership of the malloc'd buffer.
|
||||
// Note: This does not free the buffer.
|
||||
void Release() {
|
||||
CHECK(IsAllocated());
|
||||
buf_ = buf_st_;
|
||||
length_ = 0;
|
||||
capacity_ = 0;
|
||||
}
|
||||
|
||||
MaybeStackBuffer() : length_(0), capacity_(0), buf_(buf_st_) {
|
||||
// Default to a zero-length, null-terminated buffer.
|
||||
buf_[0] = T();
|
||||
}
|
||||
|
||||
explicit MaybeStackBuffer(size_t storage) : MaybeStackBuffer() {
|
||||
AllocateSufficientStorage(storage);
|
||||
}
|
||||
|
||||
~MaybeStackBuffer() {
|
||||
if (IsAllocated())
|
||||
free(buf_);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t length_;
|
||||
// capacity of the malloc'ed buf_
|
||||
size_t capacity_;
|
||||
T* buf_;
|
||||
T buf_st_[kStackStorageSize];
|
||||
};
|
||||
|
||||
class Utf8Value : public MaybeStackBuffer<char> {
|
||||
public:
|
||||
explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
};
|
||||
|
||||
class TwoByteValue : public MaybeStackBuffer<uint16_t> {
|
||||
public:
|
||||
explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
};
|
||||
|
||||
class BufferValue : public MaybeStackBuffer<char> {
|
||||
public:
|
||||
explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
};
|
||||
|
||||
#define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \
|
||||
do { \
|
||||
if (!Buffer::HasInstance(obj)) \
|
||||
return env->ThrowTypeError("argument should be a Buffer"); \
|
||||
} while (0)
|
||||
|
||||
#define SPREAD_BUFFER_ARG(val, name) \
|
||||
CHECK((val)->IsArrayBufferView()); \
|
||||
v8::Local<v8::ArrayBufferView> name = (val).As<v8::ArrayBufferView>(); \
|
||||
v8::ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \
|
||||
const size_t name##_offset = name->ByteOffset(); \
|
||||
const size_t name##_length = name->ByteLength(); \
|
||||
char* const name##_data = \
|
||||
static_cast<char*>(name##_c.Data()) + name##_offset; \
|
||||
if (name##_length > 0) \
|
||||
CHECK_NE(name##_data, nullptr);
|
||||
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#define NODE_UTIL_H_INCLUDE
|
||||
#include "util-inl.h"
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_UTIL_H_
|
||||
@@ -0,0 +1,501 @@
|
||||
0,205,180,120,218,237,125,107,111,220,56,182,224,95,33,140,11,196,1,42,
|
||||
158,153,187,192,221,197,220,139,5,18,39,153,246,69,30,134,157,204,44,176,
|
||||
200,7,149,196,114,177,163,146,106,69,201,142,103,208,255,125,207,139,15,73,
|
||||
148,74,101,187,95,51,1,26,105,151,68,145,135,228,225,121,159,195,127,156,
|
||||
20,245,46,51,149,61,249,243,255,253,199,73,94,239,118,89,85,240,143,70,
|
||||
183,93,35,47,76,171,119,240,215,63,78,254,173,209,155,147,63,159,188,166,
|
||||
143,78,126,90,157,180,247,123,13,15,178,166,201,238,79,86,39,85,182,195,
|
||||
159,174,211,213,73,161,109,222,152,125,107,234,10,158,191,51,182,85,245,70,
|
||||
217,110,191,175,155,86,23,74,90,158,157,252,244,101,117,178,133,193,75,221,
|
||||
224,152,39,235,166,190,179,186,129,46,26,93,21,186,129,63,191,248,254,111,
|
||||
116,251,122,98,136,43,6,123,106,8,254,1,237,174,243,173,222,101,163,207,
|
||||
47,155,250,214,192,35,101,170,77,221,236,50,124,172,178,117,221,181,170,221,
|
||||
106,181,111,234,182,206,235,82,89,250,252,236,132,87,128,87,9,94,238,117,
|
||||
211,26,249,41,75,99,219,198,84,55,97,109,232,127,195,97,121,54,10,223,
|
||||
1,156,171,201,143,111,97,117,240,139,137,239,229,53,79,85,186,168,215,63,
|
||||
234,188,133,47,76,17,118,110,244,125,248,133,251,211,155,42,47,25,246,137,
|
||||
128,77,226,72,191,195,55,183,89,217,241,234,53,218,118,101,123,22,38,193,
|
||||
15,224,183,96,211,149,222,213,173,254,200,112,226,24,131,174,190,229,154,33,
|
||||
43,116,155,153,210,98,87,53,61,201,202,147,63,183,77,167,125,215,218,181,
|
||||
125,205,77,195,32,111,134,111,126,10,232,164,25,88,220,150,125,214,192,179,
|
||||
150,145,112,114,27,244,183,61,76,34,185,19,111,252,43,213,214,202,245,204,
|
||||
155,58,4,122,162,119,222,177,191,52,117,183,31,117,127,125,191,91,215,165,
|
||||
201,213,13,190,38,132,129,221,202,90,149,103,149,90,107,213,89,192,120,24,
|
||||
184,209,165,206,172,86,59,88,105,179,47,181,226,78,237,44,32,235,186,134,
|
||||
175,170,0,137,169,242,178,43,244,57,239,249,59,83,233,151,151,23,9,228,
|
||||
129,245,218,193,75,171,238,182,26,112,167,81,242,133,194,79,20,124,163,236,
|
||||
182,238,202,2,33,204,110,97,249,179,53,128,84,116,56,111,66,54,237,209,
|
||||
229,56,0,173,41,117,213,142,32,186,168,20,191,81,187,186,128,222,221,222,
|
||||
91,24,12,232,74,229,134,14,195,170,172,209,170,170,91,88,56,33,27,8,
|
||||
126,81,211,179,125,6,203,10,189,232,188,35,16,213,71,56,104,13,145,137,
|
||||
255,202,97,128,255,109,117,123,137,109,62,86,30,205,254,235,15,244,70,217,
|
||||
214,111,255,96,39,247,58,55,27,67,164,6,214,205,228,219,48,130,202,235,
|
||||
170,213,223,90,220,73,32,41,72,137,226,37,82,23,114,68,29,178,42,99,
|
||||
85,189,51,45,194,221,95,78,117,103,202,18,151,93,186,129,6,48,28,182,
|
||||
113,67,200,113,7,10,9,0,97,7,251,236,70,207,28,49,249,238,162,136,
|
||||
207,150,192,125,238,223,29,179,137,76,69,94,221,255,21,128,30,147,198,191,
|
||||
9,74,33,144,76,57,112,182,112,2,25,90,88,33,68,42,245,223,215,31,
|
||||
63,8,146,243,129,8,24,103,17,17,214,247,10,23,69,31,135,95,55,186,
|
||||
210,13,108,224,101,163,111,141,190,131,23,56,112,99,118,208,101,248,58,13,
|
||||
240,158,191,137,0,113,189,21,10,182,34,154,208,113,48,1,158,53,127,209,
|
||||
22,214,76,31,5,79,192,174,0,81,219,104,130,39,67,44,52,173,161,31,
|
||||
176,84,56,134,67,148,207,23,199,193,151,221,101,166,5,62,186,51,118,122,
|
||||
55,71,192,224,71,180,44,123,254,82,54,22,22,168,46,111,117,225,113,94,
|
||||
80,0,176,54,66,114,64,8,60,167,153,146,97,87,202,180,207,44,162,184,
|
||||
53,40,59,120,52,169,20,28,220,186,17,89,32,197,177,52,225,150,35,225,
|
||||
240,223,77,89,175,179,82,80,139,87,98,154,237,201,248,110,95,213,223,240,
|
||||
244,225,137,65,238,220,232,31,25,103,9,17,149,217,248,185,222,193,250,187,
|
||||
183,79,200,40,113,8,160,63,249,87,248,183,201,114,141,235,228,201,239,147,
|
||||
115,209,193,190,15,56,233,128,64,23,128,176,72,253,154,72,220,192,15,163,
|
||||
217,203,19,158,110,76,110,226,101,248,103,38,53,79,64,86,70,88,254,178,
|
||||
40,148,136,217,196,92,28,2,154,118,171,110,204,173,174,252,35,153,160,41,
|
||||
14,225,252,121,6,56,254,219,23,242,114,0,243,109,87,229,248,250,99,117,
|
||||
60,130,186,13,175,21,246,164,54,210,149,66,145,101,32,186,29,64,214,9,
|
||||
177,207,245,248,90,231,37,128,214,38,229,252,240,206,193,229,1,17,200,120,
|
||||
187,188,198,150,216,171,172,185,233,144,97,156,5,40,241,249,75,121,60,173,
|
||||
214,185,15,109,98,111,102,198,177,103,234,37,146,193,222,51,144,76,65,23,
|
||||
92,235,178,70,17,176,166,169,88,148,102,255,27,40,212,53,117,165,238,234,
|
||||
166,36,222,132,47,91,248,84,183,61,58,252,79,41,37,254,106,164,140,101,
|
||||
208,239,98,211,119,177,233,24,177,9,79,186,13,68,40,98,36,69,76,171,
|
||||
120,77,248,133,28,97,197,68,81,52,217,122,51,192,85,83,193,132,13,237,
|
||||
58,64,55,69,2,230,172,84,151,108,143,185,119,198,141,186,153,38,109,158,
|
||||
93,245,103,39,32,6,203,78,207,58,227,250,24,146,21,224,41,149,23,25,
|
||||
163,143,213,105,93,149,247,110,170,160,161,35,254,193,154,91,93,110,158,207,
|
||||
48,60,35,61,94,6,3,211,106,52,219,139,126,163,222,172,127,13,126,11,
|
||||
59,213,131,247,193,236,150,183,56,94,198,13,162,226,131,120,110,56,117,19,
|
||||
115,5,178,223,131,122,0,38,192,134,173,5,38,27,3,197,172,12,249,5,
|
||||
109,178,176,180,254,38,175,232,188,193,43,248,201,6,54,132,75,229,91,54,
|
||||
176,29,1,103,150,231,160,162,212,77,0,246,35,140,186,136,176,141,38,225,
|
||||
250,234,97,42,31,100,221,194,134,253,193,210,255,158,211,196,254,83,57,108,
|
||||
140,155,7,190,136,93,194,153,213,6,233,213,111,145,107,216,20,21,187,26,
|
||||
239,39,224,97,246,51,16,44,79,111,200,62,247,209,25,103,31,113,58,168,
|
||||
163,163,206,67,98,250,212,9,42,161,216,210,75,4,129,152,59,53,32,5,
|
||||
190,179,84,46,179,157,46,178,110,214,241,74,139,85,124,26,106,86,64,34,
|
||||
200,45,235,101,65,186,116,59,73,29,246,103,210,85,23,155,191,1,23,5,
|
||||
32,223,214,205,107,189,238,110,110,200,239,208,31,236,147,70,62,23,172,100,
|
||||
240,87,155,85,57,49,92,232,3,85,109,224,196,168,200,223,113,103,132,118,
|
||||
133,116,71,48,180,160,138,111,123,131,235,10,53,241,177,13,153,30,91,145,
|
||||
50,137,164,108,198,198,65,96,212,40,134,224,19,16,61,118,112,148,8,105,
|
||||
89,214,212,3,147,220,57,75,44,78,222,212,183,40,255,43,56,61,149,96,
|
||||
177,27,9,240,21,228,68,2,192,217,18,145,126,57,51,34,9,133,102,183,
|
||||
211,5,10,61,64,231,112,150,58,35,227,165,177,212,195,8,208,222,148,11,
|
||||
99,147,115,126,205,207,151,79,122,216,107,158,53,5,76,22,228,30,253,166,
|
||||
2,156,75,144,239,215,220,10,37,156,178,228,109,140,132,126,20,229,115,238,
|
||||
128,76,214,168,175,8,191,79,209,33,167,91,232,246,28,52,153,122,199,103,
|
||||
225,45,57,141,224,12,240,30,22,147,199,98,68,253,100,205,197,205,50,173,
|
||||
110,95,20,142,8,240,211,25,198,205,13,98,74,112,237,158,252,42,26,120,
|
||||
189,219,131,190,197,48,60,189,175,69,186,159,245,159,217,186,107,114,253,249,
|
||||
234,221,152,244,208,27,213,53,165,8,189,176,177,158,115,184,149,78,114,104,
|
||||
103,174,66,223,155,109,253,236,166,172,253,119,145,102,38,32,23,50,64,196,
|
||||
189,164,55,93,60,133,235,64,122,7,50,245,235,186,14,244,216,79,48,235,
|
||||
67,24,43,27,188,94,177,137,246,144,117,234,170,251,61,120,32,97,107,166,
|
||||
142,197,252,233,23,246,19,205,237,168,67,255,175,130,73,191,71,231,235,63,
|
||||
165,213,234,55,239,81,254,125,153,213,126,89,19,218,239,212,68,5,44,192,
|
||||
58,202,213,83,105,144,10,57,205,32,200,170,113,148,16,124,218,154,68,188,
|
||||
142,60,151,216,24,68,128,26,21,145,200,118,221,72,139,88,52,23,29,37,
|
||||
62,153,128,209,59,131,128,123,26,161,70,161,51,172,94,123,213,26,84,140,
|
||||
222,39,76,130,24,4,255,8,118,112,165,152,182,225,177,135,142,80,120,245,
|
||||
99,118,149,249,127,232,130,12,138,229,136,142,225,142,109,186,134,246,84,122,
|
||||
5,250,10,75,14,74,15,80,137,198,220,152,96,228,98,16,113,45,208,211,
|
||||
201,244,125,7,179,109,238,97,40,96,216,100,194,191,167,70,108,25,64,120,
|
||||
129,146,154,22,84,8,33,151,192,217,26,153,169,252,206,72,125,163,45,35,
|
||||
165,55,130,197,10,7,49,77,79,87,236,7,100,13,73,60,69,64,93,7,
|
||||
254,216,223,211,207,188,38,130,40,97,105,210,2,37,245,53,80,172,39,122,
|
||||
244,158,180,126,143,186,234,118,24,233,118,81,109,208,136,140,198,155,15,217,
|
||||
7,248,247,69,244,228,197,31,79,190,172,210,131,127,174,44,40,37,89,105,
|
||||
254,142,180,47,77,175,46,65,107,1,109,244,86,139,207,153,137,17,108,51,
|
||||
30,42,216,105,36,84,47,184,91,132,76,164,205,65,32,155,3,212,71,146,
|
||||
57,107,47,252,217,85,133,222,224,142,195,223,129,149,118,187,53,169,208,129,
|
||||
130,88,98,157,169,185,8,109,161,199,19,230,87,124,151,166,84,14,52,111,
|
||||
205,237,202,18,255,7,236,137,34,7,111,0,205,176,87,14,238,218,101,248,
|
||||
3,24,26,252,123,167,179,175,252,27,255,226,103,166,69,34,89,35,228,66,
|
||||
48,233,111,34,44,40,152,53,245,183,123,254,191,80,64,4,172,224,177,167,
|
||||
103,102,187,245,220,228,228,181,218,26,212,200,157,64,198,231,143,57,48,175,
|
||||
187,99,186,212,152,118,211,146,65,238,168,208,178,188,204,172,253,144,138,64,
|
||||
20,104,168,129,58,69,250,10,29,229,176,0,207,217,254,242,212,144,101,85,
|
||||
100,125,191,77,34,239,85,207,24,37,65,19,64,169,81,180,170,49,120,162,
|
||||
135,219,22,201,7,49,94,249,121,26,76,50,141,134,115,136,218,212,243,148,
|
||||
58,53,121,72,212,196,41,1,178,175,153,47,109,51,248,138,215,130,190,149,
|
||||
165,88,169,117,215,178,41,165,221,26,111,86,188,159,145,104,187,228,105,22,
|
||||
145,54,117,212,143,216,245,120,186,35,153,54,201,35,122,102,198,212,154,77,
|
||||
145,54,117,138,216,81,213,213,139,225,246,204,57,56,22,153,240,135,155,198,
|
||||
50,142,4,215,224,28,178,245,26,31,146,152,227,86,92,6,127,24,250,78,
|
||||
194,187,159,181,80,203,20,24,120,39,171,165,182,203,29,74,178,31,93,46,
|
||||
233,243,188,215,116,50,202,183,167,207,14,119,252,125,79,120,112,60,157,12,
|
||||
110,142,169,71,114,76,140,2,201,177,206,23,64,191,44,50,122,171,51,16,
|
||||
233,102,141,44,219,204,190,170,139,251,216,244,183,113,22,183,69,129,66,242,
|
||||
209,218,84,69,252,214,5,134,28,25,102,20,66,34,55,230,230,240,248,95,
|
||||
190,51,215,9,230,250,251,224,158,79,66,71,167,80,187,6,189,123,83,214,
|
||||
99,173,238,83,67,193,130,27,101,235,157,142,194,229,188,163,10,132,105,54,
|
||||
120,251,225,250,194,185,42,64,213,65,110,181,49,237,32,58,104,224,41,15,
|
||||
196,106,194,77,190,159,246,141,186,220,142,62,116,233,241,208,64,127,120,48,
|
||||
237,205,248,243,193,70,241,200,242,77,26,21,0,37,29,30,160,18,228,237,
|
||||
27,222,158,33,200,212,199,143,41,34,219,167,239,75,156,149,78,198,74,179,
|
||||
172,102,44,238,204,144,221,225,150,61,134,240,38,83,82,220,0,81,82,202,
|
||||
19,210,170,149,247,96,63,134,108,169,151,206,117,205,58,54,41,177,17,2,
|
||||
222,139,223,157,2,95,171,145,163,251,184,211,159,22,82,63,131,100,246,98,
|
||||
3,56,87,21,160,202,246,197,14,81,192,83,242,211,7,18,71,165,153,136,
|
||||
19,51,2,7,181,11,91,189,88,194,248,23,33,225,95,38,143,73,143,210,
|
||||
44,58,35,105,49,83,200,203,87,125,63,4,17,22,233,69,105,190,106,231,
|
||||
84,68,178,239,168,208,244,134,66,71,115,251,56,11,132,144,134,17,98,166,
|
||||
123,75,9,29,15,166,3,200,108,248,4,171,168,85,10,193,63,57,64,21,
|
||||
40,148,117,206,36,206,155,114,22,40,68,131,89,141,92,52,75,141,150,119,
|
||||
141,105,147,30,103,225,171,97,73,103,33,133,125,190,71,93,48,223,102,213,
|
||||
13,52,56,133,51,147,249,69,168,27,198,197,164,126,249,50,10,143,99,99,
|
||||
176,110,110,49,108,193,162,245,145,98,107,188,89,214,13,183,82,30,251,61,
|
||||
101,117,7,128,97,110,52,27,71,93,15,167,158,186,37,128,154,92,229,27,
|
||||
189,220,13,54,59,15,251,232,121,216,199,204,195,206,206,99,10,55,88,112,
|
||||
239,154,131,248,65,52,135,142,95,164,205,71,0,14,177,3,197,11,179,73,
|
||||
34,80,161,75,221,11,18,202,235,6,68,198,125,93,21,164,131,45,16,21,
|
||||
145,170,235,67,64,199,128,218,109,125,103,85,183,247,190,33,238,96,144,238,
|
||||
233,5,202,234,0,92,139,143,94,102,63,145,79,106,118,109,197,238,127,151,
|
||||
13,61,88,41,55,206,210,248,56,99,63,30,24,54,136,8,48,227,187,42,
|
||||
114,141,76,91,61,60,45,20,26,200,45,87,201,46,249,145,200,152,212,60,
|
||||
102,96,115,168,236,36,164,36,54,79,73,163,137,176,207,249,96,214,24,125,
|
||||
147,102,223,99,152,196,121,93,97,152,16,77,39,12,16,36,199,95,146,55,
|
||||
76,173,208,76,128,236,196,74,13,35,29,123,75,166,62,245,142,152,177,213,
|
||||
179,22,200,88,179,203,74,16,4,111,141,53,232,145,52,85,108,76,193,253,
|
||||
79,174,245,195,141,164,3,227,229,147,216,55,159,218,76,57,103,214,229,100,
|
||||
164,71,217,6,167,54,188,151,212,50,54,46,139,162,30,5,175,247,50,84,
|
||||
206,212,27,246,84,245,245,50,83,244,132,210,139,194,155,124,131,193,51,101,
|
||||
16,238,47,220,208,120,141,92,178,18,207,24,80,14,50,162,122,126,249,28,
|
||||
143,197,46,118,33,59,1,180,71,19,17,91,57,114,145,37,223,84,136,68,
|
||||
42,152,4,93,167,233,144,189,89,201,248,179,115,33,122,213,123,212,137,186,
|
||||
24,133,70,48,232,247,115,225,38,226,128,139,188,164,189,176,40,14,32,137,
|
||||
196,95,115,56,246,99,42,214,131,236,36,137,176,178,33,72,220,112,54,178,
|
||||
44,73,22,127,232,118,25,122,113,179,130,54,157,36,104,110,177,166,88,203,
|
||||
129,231,121,154,181,122,196,118,158,248,238,219,107,144,63,199,160,131,190,93,
|
||||
20,160,145,10,138,228,10,90,154,210,100,13,208,46,248,96,198,156,49,92,
|
||||
186,215,51,198,174,65,185,136,12,253,244,117,201,100,28,115,182,230,248,73,
|
||||
64,211,97,196,84,2,67,67,228,149,41,102,151,31,33,158,249,26,95,175,
|
||||
70,161,24,130,148,55,28,184,64,12,200,3,19,194,55,96,139,162,164,217,
|
||||
228,129,19,32,74,56,173,31,156,201,99,104,163,170,96,255,233,93,56,47,
|
||||
110,168,178,206,25,209,79,255,248,98,141,158,239,231,243,227,128,166,217,237,
|
||||
170,137,145,206,233,229,209,99,13,172,153,124,6,47,94,79,119,240,240,184,
|
||||
211,133,246,150,174,41,199,214,150,171,119,211,16,173,36,22,132,182,245,206,
|
||||
133,54,187,192,143,204,246,98,160,82,179,142,56,53,167,77,75,214,244,102,
|
||||
81,214,52,125,242,9,191,136,230,29,158,205,133,21,58,222,178,57,46,61,
|
||||
123,177,230,54,78,32,112,148,237,142,20,176,176,150,219,108,191,215,213,172,
|
||||
8,240,128,40,206,9,130,51,138,148,28,69,129,101,20,29,59,174,197,19,
|
||||
0,62,69,243,55,26,160,158,179,245,111,172,70,88,39,123,97,236,168,232,
|
||||
60,81,152,82,239,168,121,131,37,65,248,201,236,52,236,234,110,28,9,248,
|
||||
193,159,174,157,41,75,99,53,172,103,97,149,53,152,17,0,24,230,226,252,
|
||||
151,137,212,78,0,73,58,230,35,164,244,130,202,148,96,29,35,176,11,99,
|
||||
89,30,26,122,196,57,28,15,228,172,67,208,250,193,68,114,220,107,25,145,
|
||||
205,167,32,142,227,17,242,30,185,140,199,152,19,43,223,54,169,173,162,211,
|
||||
78,198,63,206,139,112,49,96,132,159,156,96,0,10,15,226,67,93,217,99,
|
||||
244,128,7,56,162,128,136,232,210,155,44,34,114,118,166,222,98,156,149,189,
|
||||
175,114,126,32,49,10,98,158,200,100,39,135,201,229,120,182,66,174,43,190,
|
||||
227,46,18,41,231,81,78,57,175,211,164,135,39,119,77,236,163,208,254,37,
|
||||
66,2,103,190,170,187,94,12,94,76,196,105,2,32,242,231,186,32,248,221,
|
||||
162,172,150,82,221,125,214,176,30,177,132,180,159,187,204,156,77,19,173,166,
|
||||
171,14,194,114,8,108,125,209,229,178,152,21,73,190,209,52,24,106,226,98,
|
||||
46,82,114,57,168,60,142,3,194,33,235,92,92,65,216,171,41,180,191,142,
|
||||
217,91,34,219,159,102,74,185,153,17,146,43,71,157,21,188,179,217,141,150,
|
||||
236,59,74,42,98,148,159,224,42,146,176,116,40,30,254,37,172,220,29,232,
|
||||
219,185,100,100,39,52,153,97,229,162,105,110,21,139,188,227,48,210,11,107,
|
||||
59,39,88,192,160,9,205,197,88,7,71,63,203,107,60,76,219,212,247,135,
|
||||
39,23,130,253,11,247,137,10,147,120,170,164,138,120,94,201,57,249,193,103,
|
||||
103,101,207,75,13,71,36,161,101,70,253,35,162,140,62,84,119,40,131,228,
|
||||
252,57,106,134,174,246,95,111,52,97,248,222,160,56,187,114,158,115,143,164,
|
||||
197,8,31,218,136,189,203,130,5,142,159,26,123,89,74,199,220,234,58,169,
|
||||
37,146,82,56,28,151,205,32,195,21,150,230,87,250,182,254,58,147,75,54,
|
||||
100,15,128,131,54,193,25,174,232,113,172,128,222,109,239,7,64,53,60,212,
|
||||
60,139,157,211,216,208,182,199,54,1,233,42,12,176,82,153,13,57,8,176,
|
||||
207,46,85,81,222,127,118,171,32,70,147,179,3,203,233,87,109,110,14,225,
|
||||
236,99,98,223,203,203,11,36,86,169,181,116,174,210,178,190,161,73,173,59,
|
||||
138,180,5,209,51,10,1,189,203,154,138,151,186,48,13,255,251,109,135,226,
|
||||
145,115,49,181,66,32,9,155,201,21,158,53,62,155,36,252,0,37,173,204,
|
||||
246,150,224,208,85,225,26,48,225,228,16,211,141,161,14,229,175,55,21,54,
|
||||
205,235,142,88,16,98,46,62,57,210,117,254,201,123,49,244,12,223,30,232,
|
||||
18,211,181,96,236,201,129,178,47,15,208,68,28,104,180,145,187,172,136,253,
|
||||
156,15,72,250,73,128,231,79,253,49,148,32,37,231,49,135,206,179,61,150,
|
||||
20,137,20,206,17,248,15,212,23,103,113,63,78,84,197,177,114,194,234,30,
|
||||
202,75,50,213,149,139,125,29,163,124,207,194,122,216,125,54,52,66,161,195,
|
||||
254,16,201,19,205,54,216,92,66,138,23,25,57,245,55,88,226,82,175,216,
|
||||
137,24,82,64,164,217,233,115,37,5,71,89,21,112,121,185,207,147,229,72,
|
||||
151,101,85,251,156,108,231,239,145,186,87,152,113,166,206,75,67,54,97,1,
|
||||
152,114,79,96,58,46,209,139,121,49,126,143,228,115,155,97,217,7,36,236,
|
||||
146,47,141,242,126,25,59,181,120,8,148,11,100,22,6,201,83,174,205,237,
|
||||
96,179,14,166,69,247,160,142,32,142,59,177,186,125,5,180,255,235,190,198,
|
||||
157,121,153,163,125,121,121,2,114,230,218,15,35,85,238,196,70,141,35,175,
|
||||
67,255,138,63,240,89,96,227,90,99,248,158,42,234,253,1,192,207,252,47,
|
||||
60,31,113,63,226,111,76,77,231,250,171,217,191,44,75,74,64,179,203,167,
|
||||
98,225,179,3,19,193,38,156,252,102,167,103,240,62,251,10,175,17,48,194,
|
||||
4,242,7,53,29,70,197,34,223,190,119,223,159,134,217,172,98,118,87,212,
|
||||
187,136,53,233,54,127,126,40,91,53,72,124,78,160,13,93,247,50,114,66,
|
||||
42,78,152,117,104,26,83,198,87,241,211,20,189,127,39,230,179,105,90,239,
|
||||
12,108,243,65,127,190,21,99,124,4,184,83,96,112,1,107,213,237,113,245,
|
||||
138,194,180,190,84,113,10,127,95,221,127,38,195,67,122,203,31,106,123,109,
|
||||
41,206,32,6,46,107,207,158,208,42,137,83,197,68,114,155,24,233,64,42,
|
||||
98,98,164,43,12,208,74,200,113,240,20,112,15,195,37,42,79,197,0,8,
|
||||
187,0,10,60,110,222,181,37,129,25,141,119,77,215,209,51,26,198,189,144,
|
||||
106,109,67,215,211,196,84,142,51,202,124,220,108,16,70,73,9,38,58,255,
|
||||
232,109,66,123,156,105,15,23,13,192,228,87,98,61,209,80,254,91,41,144,
|
||||
225,231,188,10,84,152,50,154,169,208,143,109,235,189,35,97,81,39,46,6,
|
||||
35,42,38,170,125,133,81,44,14,212,116,73,146,115,141,73,35,145,13,163,
|
||||
183,4,66,248,189,13,223,67,230,178,234,214,247,138,144,177,161,255,81,124,
|
||||
223,153,250,72,101,74,6,92,200,16,147,94,137,70,38,165,59,224,172,89,
|
||||
95,141,192,242,36,41,197,37,198,31,127,154,177,39,159,149,232,197,121,79,
|
||||
6,28,234,120,127,190,122,43,196,107,151,181,249,54,50,205,226,176,164,142,
|
||||
24,95,73,18,251,179,221,218,162,240,2,83,231,174,3,20,87,2,67,175,
|
||||
158,137,155,148,184,230,65,144,55,32,43,196,43,72,3,216,174,185,69,190,
|
||||
69,100,189,209,101,157,21,246,183,64,151,7,68,203,237,241,44,49,61,235,
|
||||
177,239,46,43,61,49,95,141,201,251,4,161,61,164,75,7,72,70,135,210,
|
||||
196,234,116,57,51,244,191,252,193,205,6,71,119,80,77,105,87,223,234,185,
|
||||
29,57,2,147,190,164,210,232,110,245,4,108,211,245,109,230,42,235,209,136,
|
||||
79,37,55,236,107,203,177,50,209,138,249,47,207,134,37,229,164,113,36,231,
|
||||
30,194,223,107,212,186,201,32,129,177,121,140,197,89,131,6,216,185,129,251,
|
||||
184,77,154,251,4,98,15,181,140,226,33,99,169,83,16,20,203,14,67,237,
|
||||
158,11,246,162,196,25,97,176,230,142,29,109,6,12,37,63,103,102,221,27,
|
||||
26,114,206,111,87,21,199,29,205,68,109,4,60,173,121,251,169,126,27,2,
|
||||
255,7,188,28,79,150,203,206,143,102,200,38,111,204,238,22,54,79,181,94,
|
||||
79,49,81,176,226,204,204,96,230,207,72,34,111,218,217,66,109,110,61,195,
|
||||
16,125,245,228,76,57,111,23,177,18,194,1,100,88,184,88,188,57,225,203,
|
||||
168,176,167,64,54,180,29,1,119,236,244,167,58,162,174,139,73,166,251,90,
|
||||
245,168,245,145,196,210,199,241,83,64,88,222,53,228,139,152,49,255,80,225,
|
||||
185,243,216,189,50,62,227,169,250,58,12,171,141,12,193,172,211,250,120,18,
|
||||
47,123,144,38,155,229,219,129,34,11,123,185,199,18,34,9,191,148,222,131,
|
||||
240,121,43,213,51,72,231,226,42,196,131,175,47,128,175,77,124,77,250,67,
|
||||
207,47,21,204,88,241,248,93,59,53,124,215,142,92,91,163,46,72,171,75,
|
||||
116,80,239,189,174,74,238,154,190,167,41,53,27,92,157,174,212,215,50,43,
|
||||
242,83,45,202,20,138,166,203,158,33,233,169,112,142,187,204,126,37,111,80,
|
||||
69,193,166,225,245,90,195,49,16,248,104,30,103,42,220,136,67,129,213,98,
|
||||
136,143,186,33,77,190,163,176,69,223,81,168,14,201,238,27,24,171,170,185,
|
||||
61,89,235,195,136,232,239,169,184,52,67,114,190,83,230,41,148,243,82,149,
|
||||
53,232,113,143,91,13,92,245,199,50,171,107,162,192,239,81,230,60,186,254,
|
||||
171,191,176,136,169,56,9,174,218,14,148,86,124,117,193,134,199,234,65,5,
|
||||
156,164,119,51,95,198,73,106,142,156,45,241,217,131,212,220,220,79,185,134,
|
||||
195,136,155,250,200,18,157,152,123,127,173,129,186,39,77,69,190,178,168,155,
|
||||
144,229,100,125,235,190,56,178,86,144,77,235,191,126,24,42,195,108,93,153,
|
||||
147,80,136,138,124,0,168,250,36,197,52,4,77,28,149,44,147,201,247,200,
|
||||
38,92,76,0,109,229,88,41,152,207,67,68,3,83,47,110,72,106,21,232,
|
||||
130,11,66,162,185,208,197,215,32,67,4,238,245,215,247,116,60,80,20,221,
|
||||
115,208,205,2,71,249,140,163,253,168,250,82,8,234,57,39,44,76,86,243,
|
||||
17,86,195,70,109,158,28,219,182,235,130,21,207,108,67,43,190,223,151,247,
|
||||
46,102,159,147,32,236,164,167,62,94,164,149,80,177,25,185,133,72,213,117,
|
||||
194,88,238,15,196,178,32,171,232,82,140,249,1,167,253,125,110,196,185,82,
|
||||
110,104,181,164,241,185,162,224,67,232,1,34,204,147,145,2,219,7,102,140,
|
||||
178,130,237,195,170,146,199,160,82,209,220,3,72,163,222,149,156,212,8,43,
|
||||
88,255,34,179,186,227,57,24,116,2,232,131,54,37,245,186,185,167,106,170,
|
||||
18,140,226,162,134,177,136,174,216,5,162,17,40,98,19,57,186,239,138,176,
|
||||
50,224,33,71,219,143,111,122,41,76,95,85,99,99,153,42,61,133,154,99,
|
||||
39,143,11,107,25,208,136,95,229,140,124,233,9,244,32,24,187,0,145,89,
|
||||
68,13,65,30,189,178,81,225,142,179,254,77,24,126,13,98,188,61,143,30,
|
||||
166,100,123,130,6,13,238,77,107,242,174,204,26,185,107,131,70,245,9,83,
|
||||
107,125,99,170,202,39,214,198,155,245,144,51,208,67,130,161,190,249,216,163,
|
||||
140,152,43,232,213,175,90,190,228,80,79,106,63,161,199,94,136,235,100,185,
|
||||
230,68,13,63,59,237,1,7,165,76,83,70,119,158,117,55,91,148,104,96,
|
||||
19,230,242,123,91,78,31,30,100,157,80,97,193,186,138,43,20,238,38,78,
|
||||
228,107,74,116,176,82,140,176,255,13,59,103,212,57,231,17,160,1,10,229,
|
||||
23,177,221,176,245,210,181,197,44,11,134,57,238,128,234,209,68,15,206,212,
|
||||
5,197,177,149,211,195,161,228,194,38,71,92,139,16,142,48,107,35,148,164,
|
||||
161,187,6,89,124,240,46,234,99,110,66,20,20,248,69,234,145,46,97,102,
|
||||
238,108,127,172,226,168,199,95,150,78,172,126,139,215,48,178,200,56,42,83,
|
||||
78,181,82,187,54,24,177,225,247,41,76,6,211,47,1,47,76,33,197,237,
|
||||
184,84,8,182,137,186,225,104,22,180,235,224,67,198,191,113,161,117,193,198,
|
||||
231,63,71,221,205,113,149,226,145,247,61,85,118,211,221,117,32,171,92,68,
|
||||
182,83,180,188,110,50,42,98,8,173,54,89,105,245,247,42,168,223,111,60,
|
||||
60,14,38,218,210,143,213,53,236,199,155,205,134,99,80,150,67,69,216,9,
|
||||
29,112,162,155,207,39,2,165,222,32,230,80,135,81,73,196,134,173,26,112,
|
||||
136,71,40,116,204,181,129,190,174,168,39,135,67,142,252,215,172,49,113,18,
|
||||
229,66,71,183,205,235,253,148,167,91,162,215,163,228,31,106,173,232,86,2,
|
||||
11,135,216,74,228,29,63,230,123,71,20,217,108,159,161,97,175,124,182,82,
|
||||
207,242,178,182,93,163,159,209,249,120,150,163,201,227,153,124,64,165,53,233,
|
||||
32,17,77,67,137,253,163,88,126,106,124,147,187,125,7,154,97,246,29,103,
|
||||
132,193,223,36,157,207,95,108,44,139,145,76,129,112,43,53,25,0,206,161,
|
||||
29,210,104,88,30,163,210,119,131,60,85,199,249,6,183,158,165,229,57,220,
|
||||
64,102,103,116,200,182,117,89,88,63,216,227,4,94,86,190,173,75,1,221,
|
||||
132,57,80,97,90,63,178,187,121,68,118,87,22,219,209,179,112,211,53,85,
|
||||
147,21,71,253,174,107,19,171,31,176,143,180,12,4,143,52,131,215,112,42,
|
||||
182,203,113,112,151,125,115,95,12,3,102,190,153,93,183,3,162,15,111,41,
|
||||
53,208,39,9,176,70,131,149,153,116,219,138,25,138,105,235,31,29,37,37,
|
||||
221,144,15,36,156,29,64,74,137,138,242,165,85,176,98,210,176,67,117,42,
|
||||
28,230,121,242,120,74,208,23,150,147,112,161,84,227,46,80,205,250,234,21,
|
||||
138,249,219,30,94,149,208,118,93,127,187,228,248,139,132,28,237,53,198,62,
|
||||
174,79,23,213,10,61,13,52,65,108,199,161,180,88,168,71,234,27,185,98,
|
||||
234,78,57,206,183,26,111,248,100,29,0,239,47,32,71,136,64,57,29,219,
|
||||
116,165,247,37,90,169,136,104,99,198,129,255,196,193,195,10,197,30,131,82,
|
||||
225,36,84,88,85,235,109,77,81,37,107,104,137,142,20,148,196,49,142,10,
|
||||
141,238,123,152,227,31,144,143,246,108,105,210,9,130,229,189,254,208,149,247,
|
||||
253,201,80,103,100,9,195,137,97,238,12,116,11,12,0,195,14,4,164,112,
|
||||
67,2,48,53,201,1,198,158,158,225,192,48,24,208,39,13,12,59,147,232,
|
||||
206,149,218,96,249,51,42,45,108,229,50,17,232,147,91,3,101,127,134,212,
|
||||
191,171,196,32,190,233,202,99,246,93,23,87,116,106,143,213,6,143,55,232,
|
||||
140,12,217,244,242,178,182,102,222,237,186,151,22,118,50,162,205,237,159,223,
|
||||
60,235,221,114,146,54,86,197,107,223,176,61,175,183,71,209,6,9,91,113,
|
||||
27,254,51,109,145,114,19,71,202,135,231,66,202,168,145,47,208,56,47,35,
|
||||
250,38,250,184,79,86,104,182,114,2,254,154,198,74,28,31,16,93,41,218,
|
||||
16,102,122,166,248,200,69,146,148,75,22,77,232,172,18,81,48,93,3,60,
|
||||
68,138,230,217,62,91,155,210,80,73,60,76,139,23,157,192,10,45,228,80,
|
||||
154,93,125,139,63,162,96,155,149,63,90,36,195,128,106,27,132,212,21,213,
|
||||
208,174,57,199,49,88,139,224,27,221,230,136,109,190,138,121,239,82,163,61,
|
||||
108,60,214,251,164,140,51,95,224,60,153,147,195,211,189,164,160,160,227,239,
|
||||
168,138,130,123,116,241,100,230,204,201,248,187,166,151,184,214,27,156,74,1,
|
||||
103,213,253,129,204,65,178,62,161,106,148,142,32,172,57,76,173,63,0,82,
|
||||
55,83,245,130,238,98,59,12,66,70,113,206,206,34,148,221,216,37,80,112,
|
||||
18,247,84,110,247,3,33,57,144,212,193,106,225,120,238,153,149,60,204,132,
|
||||
129,120,166,179,137,41,188,211,213,13,139,6,20,236,55,215,249,148,110,234,
|
||||
188,52,46,199,110,156,233,53,151,41,16,204,30,199,148,138,216,102,118,155,
|
||||
170,122,67,150,115,124,121,132,249,124,24,71,63,132,246,229,195,139,59,28,
|
||||
229,93,123,7,136,129,182,240,69,218,212,39,242,184,185,224,40,199,38,108,
|
||||
164,225,13,34,248,57,150,243,150,221,95,10,67,0,15,223,198,145,190,174,
|
||||
233,125,182,79,221,216,36,81,183,130,234,187,108,63,170,38,36,80,246,79,
|
||||
255,210,245,129,77,189,142,110,139,122,224,10,161,207,207,95,58,117,236,6,
|
||||
189,175,209,155,254,168,237,121,115,253,31,104,33,233,74,125,92,192,110,73,
|
||||
199,116,217,208,209,136,252,217,129,212,116,180,221,50,195,242,217,177,33,249,
|
||||
167,79,184,229,242,57,204,150,107,12,178,48,93,60,168,28,195,76,234,235,
|
||||
132,135,164,15,61,200,13,46,163,229,214,52,232,109,2,132,67,105,86,51,
|
||||
164,110,1,36,244,148,193,198,192,138,210,214,32,233,52,98,0,65,149,227,
|
||||
107,21,178,254,194,61,116,78,120,162,224,120,74,39,65,182,238,162,22,7,
|
||||
81,37,216,244,45,149,70,248,84,19,119,254,206,156,191,51,231,239,204,249,
|
||||
247,203,156,191,243,189,239,124,239,247,198,247,54,20,76,131,254,46,4,121,
|
||||
116,188,70,17,235,46,101,227,16,175,10,145,221,227,91,170,30,155,84,241,
|
||||
146,2,69,82,49,216,11,67,115,231,22,40,206,33,136,178,100,240,230,223,
|
||||
74,66,84,220,46,147,71,45,149,9,64,126,168,98,198,164,248,36,65,40,
|
||||
231,33,152,139,138,31,15,118,86,162,209,124,190,154,139,11,248,63,63,92,
|
||||
65,87,175,63,190,135,127,223,32,130,98,116,36,138,255,132,104,161,38,149,
|
||||
207,98,39,241,69,92,142,225,222,164,43,253,163,118,193,227,31,169,51,138,
|
||||
30,197,15,119,107,115,211,213,157,157,9,54,152,168,109,192,209,6,252,242,
|
||||
40,250,94,164,136,249,184,80,63,109,110,138,182,15,175,58,152,15,17,252,
|
||||
193,244,115,241,46,94,219,201,152,191,173,105,251,89,14,83,118,229,95,39,
|
||||
132,104,242,28,204,163,84,47,33,178,137,235,237,53,20,205,44,145,196,72,
|
||||
43,115,188,77,189,49,89,34,108,120,28,176,120,0,0,249,172,23,75,252,
|
||||
229,224,93,113,175,250,68,102,146,80,45,186,44,238,188,231,32,90,16,52,
|
||||
113,54,151,22,230,236,181,44,5,112,168,219,68,229,158,56,107,97,174,162,
|
||||
231,245,240,230,187,97,189,142,80,208,216,89,20,207,98,19,161,143,147,121,
|
||||
160,54,241,208,156,222,190,233,122,80,38,236,73,50,84,251,53,21,167,199,
|
||||
251,242,52,27,54,48,244,31,115,181,199,220,26,46,174,154,150,154,71,92,
|
||||
197,56,184,180,149,119,81,229,253,26,80,187,80,10,131,200,208,228,92,123,
|
||||
113,69,115,216,153,62,34,172,111,71,40,107,184,86,60,250,84,141,139,178,
|
||||
78,17,4,35,209,102,197,19,197,34,205,214,240,251,16,41,191,169,194,102,
|
||||
156,30,161,92,182,231,48,102,224,40,140,154,32,239,110,172,136,24,204,8,
|
||||
135,51,9,110,7,134,95,150,223,52,118,109,193,206,79,203,50,228,241,62,
|
||||
199,104,133,68,244,166,143,100,136,170,109,204,46,32,147,48,108,232,211,222,
|
||||
153,223,39,191,143,162,80,140,61,58,84,47,148,58,95,107,14,250,226,124,
|
||||
105,95,51,222,227,0,90,139,90,121,175,36,55,115,114,55,185,217,68,88,
|
||||
195,160,28,122,170,186,43,174,89,239,2,143,73,74,84,239,19,39,211,73,
|
||||
132,55,101,189,206,208,228,67,193,35,88,31,201,144,242,36,33,36,248,23,
|
||||
165,221,172,78,214,208,226,43,150,64,114,183,156,99,84,13,94,57,195,170,
|
||||
222,145,133,140,174,125,52,74,106,127,63,186,155,237,164,194,183,139,7,39,
|
||||
44,226,242,135,140,2,12,254,248,74,40,156,133,143,238,226,96,11,19,245,
|
||||
103,185,252,33,75,244,188,94,255,25,199,55,69,198,31,252,22,175,44,166,
|
||||
221,109,144,68,229,24,120,10,50,89,101,13,5,221,51,172,254,66,6,74,
|
||||
138,175,233,130,27,14,5,161,235,53,48,94,61,22,52,87,147,165,123,166,
|
||||
176,114,161,225,161,98,149,226,136,51,47,42,44,3,205,241,219,179,186,105,
|
||||
211,190,155,39,13,15,27,89,99,13,160,185,212,215,217,196,248,233,82,173,
|
||||
190,32,236,209,34,137,183,243,245,178,137,38,214,29,187,13,9,100,137,126,
|
||||
201,174,67,17,20,81,127,147,165,26,163,140,183,69,217,134,81,118,27,23,
|
||||
46,213,62,26,126,234,98,165,126,10,248,3,110,86,250,46,109,62,86,218,
|
||||
156,201,19,118,142,131,107,151,151,74,164,152,238,254,98,214,113,136,226,194,
|
||||
153,120,104,125,45,87,154,140,195,15,48,25,16,75,35,227,28,92,13,80,
|
||||
21,249,61,48,40,184,113,17,196,57,85,223,234,221,153,30,176,64,190,126,
|
||||
89,20,161,238,71,85,19,77,29,27,82,14,214,211,26,66,137,97,80,114,
|
||||
27,68,35,145,207,220,192,3,77,249,39,142,139,11,154,246,224,238,39,141,
|
||||
99,1,192,247,242,237,24,12,185,64,120,203,65,103,95,146,182,69,9,44,
|
||||
161,10,156,88,185,150,12,188,47,168,242,133,187,127,30,206,234,187,26,195,
|
||||
173,108,171,51,18,101,67,83,127,214,93,24,200,57,207,232,216,40,144,120,
|
||||
221,15,6,255,247,23,77,66,38,93,153,180,12,187,136,14,172,180,138,100,
|
||||
109,254,92,150,109,65,13,214,193,38,17,155,229,65,250,218,253,132,252,194,
|
||||
181,27,127,4,137,220,203,37,149,110,239,234,230,43,21,91,164,190,95,100,
|
||||
123,67,181,27,235,134,97,205,246,251,28,243,222,233,44,193,42,202,233,177,
|
||||
58,239,26,190,180,221,217,179,220,94,48,133,196,110,245,236,61,121,233,252,
|
||||
36,89,13,21,147,228,126,177,202,80,149,210,213,169,236,149,175,156,30,177,
|
||||
132,221,46,167,7,196,128,45,211,222,31,127,241,130,235,225,224,189,22,71,
|
||||
86,215,114,219,28,223,199,177,216,178,63,233,59,236,19,90,207,183,9,119,
|
||||
67,120,3,23,201,22,0,78,255,244,8,178,191,140,224,31,13,199,100,189,
|
||||
242,254,161,90,205,31,89,111,197,112,84,227,146,11,144,206,5,143,173,66,
|
||||
132,217,151,213,28,203,72,16,232,65,189,63,172,6,9,24,113,33,17,122,
|
||||
203,67,146,77,248,34,145,113,41,221,70,145,127,149,218,153,188,169,165,114,
|
||||
255,217,76,116,246,249,229,103,37,85,88,155,113,79,103,234,189,68,92,139,
|
||||
18,47,21,24,162,175,168,228,99,221,20,18,168,215,180,163,146,25,88,92,
|
||||
102,62,185,236,138,122,224,139,205,177,207,179,222,157,188,82,43,54,220,230,
|
||||
75,15,226,164,228,182,222,31,140,113,69,48,46,1,84,44,29,142,117,58,
|
||||
24,91,6,235,191,188,82,64,89,158,75,185,218,17,182,35,243,199,219,88,
|
||||
59,68,105,214,182,169,180,45,242,137,251,26,3,83,13,86,5,85,207,114,
|
||||
132,67,23,207,144,209,61,195,96,119,247,96,58,214,155,138,188,99,149,117,
|
||||
210,17,114,153,200,153,114,83,34,111,44,73,185,163,18,19,97,247,124,4,
|
||||
70,178,51,151,158,108,42,188,84,2,175,243,59,83,111,162,47,88,144,104,
|
||||
58,74,82,85,184,98,59,243,119,170,221,13,157,112,168,167,197,106,81,113,
|
||||
181,237,14,241,201,14,240,162,222,143,247,227,176,56,47,50,206,212,66,240,
|
||||
107,22,99,40,185,204,42,128,84,99,156,45,58,52,6,64,9,238,202,237,
|
||||
5,28,181,42,77,70,115,59,156,60,205,203,237,103,115,108,57,142,241,30,
|
||||
146,244,37,53,10,228,14,162,94,10,111,155,125,213,15,89,67,135,164,249,
|
||||
146,17,87,7,54,85,93,38,177,168,210,186,32,15,42,149,216,235,81,134,
|
||||
223,212,26,222,232,246,149,182,237,155,13,52,110,127,190,85,68,91,174,30,
|
||||
52,29,29,52,85,80,37,37,117,147,53,107,108,21,46,221,61,32,242,77,
|
||||
242,141,177,51,102,116,179,89,229,113,123,224,115,114,182,187,132,23,57,143,
|
||||
236,218,137,180,35,231,188,93,46,59,108,77,235,232,233,225,133,15,247,211,
|
||||
16,195,210,214,71,26,208,93,171,104,60,201,184,152,81,189,143,43,130,59,
|
||||
99,125,207,70,59,128,104,218,227,188,53,101,1,59,122,240,194,246,115,108,
|
||||
200,96,152,194,30,37,32,22,26,26,94,165,125,192,159,182,206,3,140,83,
|
||||
98,77,13,121,134,39,82,140,97,222,226,234,47,100,13,84,140,110,114,109,
|
||||
190,114,52,107,81,99,118,128,123,217,247,211,139,111,230,147,201,191,166,74,
|
||||
178,92,70,239,47,80,6,255,105,53,121,199,163,44,226,225,77,125,89,73,
|
||||
230,67,136,254,113,112,168,22,1,57,155,189,30,20,101,130,15,124,241,247,
|
||||
232,86,83,124,199,104,174,126,160,4,55,68,6,232,90,199,119,47,173,34,
|
||||
218,134,169,21,88,52,52,103,198,144,251,29,77,223,107,51,90,159,8,156,
|
||||
73,124,162,238,146,187,92,74,89,166,125,4,185,245,217,29,48,22,42,130,
|
||||
36,68,215,117,27,29,223,9,163,30,145,222,79,102,55,181,52,94,112,11,
|
||||
197,227,19,2,228,116,255,32,52,31,232,157,146,169,22,245,237,214,104,24,
|
||||
224,105,227,195,142,167,90,54,99,218,22,203,109,19,113,5,254,160,31,28,
|
||||
22,103,229,37,97,20,222,218,59,214,243,127,204,114,164,236,14,158,225,132,
|
||||
248,28,210,110,21,186,4,58,79,1,51,120,57,215,173,207,38,119,123,235,
|
||||
55,103,102,46,184,116,175,177,163,249,233,28,56,28,83,7,227,168,219,12,
|
||||
147,106,230,181,43,48,19,221,102,245,167,69,183,89,181,66,96,14,145,247,
|
||||
172,5,66,185,238,34,155,148,13,99,206,209,133,33,161,90,100,47,246,209,
|
||||
158,89,156,106,156,4,37,83,57,172,26,218,177,6,36,235,168,85,37,36,
|
||||
224,58,209,11,238,244,146,145,36,18,215,123,102,168,254,144,171,44,57,31,
|
||||
31,251,36,67,65,63,135,174,42,155,209,143,6,151,25,117,81,133,38,30,
|
||||
83,234,125,206,232,253,44,73,81,186,226,66,113,109,40,166,101,195,193,126,
|
||||
137,59,237,70,193,110,189,137,76,203,187,46,45,51,121,246,248,45,90,74,
|
||||
49,235,191,231,121,37,223,74,79,238,156,89,85,39,243,29,41,7,143,23,
|
||||
54,177,6,11,110,221,61,250,134,191,199,134,239,47,184,233,111,184,95,163,
|
||||
37,154,220,50,55,241,241,174,185,62,172,11,194,11,62,33,23,173,239,76,
|
||||
203,75,119,110,160,55,61,193,190,249,120,215,164,221,92,236,199,194,68,174,
|
||||
89,189,91,124,135,147,153,45,10,142,1,55,220,253,153,240,201,211,231,179,
|
||||
209,23,222,133,246,144,154,220,173,105,167,185,163,162,183,46,77,28,147,192,
|
||||
42,127,5,144,212,191,29,194,153,170,202,88,181,193,170,239,88,127,48,155,
|
||||
25,235,244,99,169,142,51,234,117,92,198,181,191,254,111,77,101,236,246,103,
|
||||
220,128,55,85,241,160,61,56,104,197,251,53,247,233,160,19,240,128,69,151,
|
||||
100,55,41,174,240,131,206,246,28,145,96,31,110,86,164,74,13,47,203,80,
|
||||
89,156,33,140,44,102,79,58,28,59,250,96,5,111,176,180,203,84,37,82,
|
||||
245,140,219,225,144,215,85,182,183,219,218,127,243,204,221,77,224,202,54,4,
|
||||
75,62,7,167,89,249,0,113,156,117,86,180,89,85,33,148,213,21,167,224,
|
||||
67,32,133,66,123,211,198,15,226,177,127,143,243,29,76,73,236,58,127,97,
|
||||
51,207,1,227,248,155,71,151,88,235,25,187,248,225,171,251,128,65,23,135,
|
||||
46,207,138,57,110,188,42,254,243,167,170,59,118,191,91,215,165,201,123,37,
|
||||
199,144,17,230,92,30,207,213,4,17,203,174,218,97,133,49,180,164,115,167,
|
||||
118,176,202,89,81,92,184,251,184,194,108,15,57,121,177,101,216,70,119,119,
|
||||
181,47,76,69,21,27,184,214,125,228,199,255,183,111,163,90,98,209,46,109,
|
||||
251,75,61,187,146,95,14,5,30,208,252,55,218,71,21,176,189,43,46,142,
|
||||
168,110,77,134,16,157,90,173,149,36,40,168,119,174,196,25,242,249,29,122,
|
||||
1,92,197,88,104,233,37,149,35,174,112,218,166,214,9,56,26,160,228,30,
|
||||
111,170,224,146,23,92,6,133,136,114,67,56,25,86,116,176,64,163,165,56,
|
||||
188,80,1,169,231,145,249,80,186,168,43,51,198,5,45,105,98,97,223,251,
|
||||
133,45,199,7,34,117,228,8,190,33,167,112,222,191,197,244,107,100,200,25,
|
||||
187,15,7,38,52,145,231,88,87,237,185,3,215,247,45,218,144,46,107,99,
|
||||
233,150,76,99,89,141,149,216,76,58,89,78,191,243,38,15,182,100,72,1,
|
||||
34,9,244,132,198,255,227,223,255,231,127,252,47,233,81,230,185,192,181,231,
|
||||
125,139,180,190,11,28,125,110,189,112,111,39,156,126,126,73,233,216,56,159,
|
||||
110,244,193,161,162,32,9,25,121,44,234,2,29,137,81,240,124,219,85,95,
|
||||
23,11,90,57,181,238,97,3,185,82,4,64,219,207,254,152,226,59,203,61,
|
||||
197,5,150,55,157,181,185,212,56,217,99,82,22,55,78,184,236,205,2,243,
|
||||
103,175,181,174,166,143,221,164,17,105,248,229,140,113,49,220,93,153,8,151,
|
||||
217,196,103,53,212,185,10,33,57,78,170,110,41,131,77,202,2,53,250,6,
|
||||
139,239,226,125,0,20,187,149,121,87,77,184,63,143,146,131,173,14,87,60,
|
||||
26,174,63,54,160,111,254,178,77,170,5,13,103,133,239,250,221,58,109,60,
|
||||
34,148,6,175,169,162,110,57,251,145,64,162,90,81,66,142,137,6,182,89,
|
||||
107,63,239,11,244,27,15,4,13,46,122,200,254,219,140,244,136,225,50,242,
|
||||
23,61,37,97,208,231,225,98,94,7,189,32,182,215,219,180,5,31,78,1,
|
||||
122,180,128,132,188,201,242,173,251,233,110,231,37,219,218,6,104,21,93,42,
|
||||
17,217,75,101,124,103,224,118,77,224,121,161,191,173,88,65,38,43,107,220,
|
||||
50,83,132,211,193,140,228,16,194,17,52,215,13,119,208,110,77,147,252,222,
|
||||
154,191,235,62,75,24,119,113,246,116,120,136,14,26,196,64,213,241,134,227,
|
||||
80,49,66,184,33,237,146,92,174,31,210,28,116,145,104,179,192,48,243,36,
|
||||
190,192,41,182,166,203,205,53,44,253,24,159,202,232,18,27,220,27,199,199,
|
||||
252,174,84,82,29,149,175,22,82,206,69,151,54,220,36,248,201,188,135,38,
|
||||
242,248,77,121,248,230,156,82,83,227,141,164,94,199,24,105,123,150,187,171,
|
||||
178,50,220,148,119,156,191,42,80,135,98,150,221,242,242,28,49,191,25,31,
|
||||
195,23,236,7,228,19,170,103,12,155,178,203,126,172,27,104,241,39,76,147,
|
||||
48,21,253,253,239,39,63,253,244,255,1,17,146,119,172
|
||||
Reference in New Issue
Block a user