no message
This commit is contained in:
597
cocos/bindings/jswrapper/v8/debugger/SHA1.cpp
Normal file
597
cocos/bindings/jswrapper/v8/debugger/SHA1.cpp
Normal file
@@ -0,0 +1,597 @@
|
||||
/* -*- 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
|
||||
69
cocos/bindings/jswrapper/v8/debugger/SHA1.h
Normal file
69
cocos/bindings/jswrapper/v8/debugger/SHA1.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* -*- 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.h"
|
||||
#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
|
||||
188
cocos/bindings/jswrapper/v8/debugger/base64.h
Normal file
188
cocos/bindings/jswrapper/v8/debugger/base64.h
Normal file
@@ -0,0 +1,188 @@
|
||||
#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(do {} while (false) /* empty*/);
|
||||
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 = static_cast<unsigned>(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_
|
||||
117
cocos/bindings/jswrapper/v8/debugger/env.cpp
Normal file
117
cocos/bindings/jswrapper/v8/debugger/env.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#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
|
||||
618
cocos/bindings/jswrapper/v8/debugger/env.h
Normal file
618
cocos/bindings/jswrapper/v8/debugger/env.h
Normal file
@@ -0,0 +1,618 @@
|
||||
#pragma once
|
||||
// clang-format off
|
||||
#include "../../config.h"
|
||||
#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),
|
||||
inspector_agent_(this),
|
||||
makecallback_cntr_(0),
|
||||
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(isolate()->GetCurrentContext(), name_string, function).Check();
|
||||
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
|
||||
|
||||
// clang-format on
|
||||
2409
cocos/bindings/jswrapper/v8/debugger/http_parser.cpp
Normal file
2409
cocos/bindings/jswrapper/v8/debugger/http_parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
353
cocos/bindings/jswrapper/v8/debugger/http_parser.h
Normal file
353
cocos/bindings/jswrapper/v8/debugger/http_parser.h
Normal file
@@ -0,0 +1,353 @@
|
||||
/* 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.h"
|
||||
#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 <cstdint>
|
||||
#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
|
||||
|
||||
using http_parser = struct http_parser;
|
||||
using http_parser_settings = struct 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.
|
||||
*/
|
||||
using http_data_cb = int (*)(http_parser *, const char *, size_t);
|
||||
using http_cb = int (*)(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 { //NOLINT(readability-identifier-naming)
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, //NOLINT(readability-identifier-naming)
|
||||
HTTP_RESPONSE,
|
||||
HTTP_BOTH };
|
||||
|
||||
/* Flag values for http_parser.flags field */
|
||||
enum flags { F_CHUNKED = 1 << 0, //NOLINT(readability-identifier-naming)
|
||||
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 { //NOLINT(readability-identifier-naming)
|
||||
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; //NOLINT(google-runtime-int)
|
||||
unsigned short http_minor; //NOLINT(google-runtime-int)
|
||||
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, //NOLINT(readability-identifier-naming)
|
||||
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 { //NOLINT(readability-identifier-naming)
|
||||
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); //NOLINT(readability-identifier-naming,google-runtime-int)
|
||||
|
||||
void http_parser_init(http_parser *parser, enum http_parser_type type); //NOLINT(readability-identifier-naming)
|
||||
|
||||
/* Initialize http_parser_settings members to 0
|
||||
*/
|
||||
void http_parser_settings_init(http_parser_settings *settings); //NOLINT(readability-identifier-naming)
|
||||
|
||||
/* Executes the parser. Returns number of parsed bytes. Sets
|
||||
* `parser->http_errno` on error. */
|
||||
size_t http_parser_execute(http_parser *parser, //NOLINT(readability-identifier-naming)
|
||||
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); //NOLINT(readability-identifier-naming)
|
||||
|
||||
/* Returns a string version of the HTTP method. */
|
||||
const char *http_method_str(enum http_method m); //NOLINT(readability-identifier-naming)
|
||||
|
||||
/* Return a string name of the given error */
|
||||
const char *http_errno_name(enum http_errno err); //NOLINT(readability-identifier-naming)
|
||||
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err); //NOLINT(readability-identifier-naming)
|
||||
|
||||
/* Initialize all http_parser_url members to 0 */
|
||||
void http_parser_url_init(struct http_parser_url *u); //NOLINT(readability-identifier-naming)
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen, //NOLINT(readability-identifier-naming)
|
||||
int is_connect, //NOLINT(readability-identifier-naming)
|
||||
struct http_parser_url *u);
|
||||
|
||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
||||
void http_parser_pause(http_parser *parser, int paused); //NOLINT(readability-identifier-naming)
|
||||
|
||||
/* Checks if this is the final chunk of the body. */
|
||||
int http_body_is_final(const http_parser *parser); //NOLINT(readability-identifier-naming)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif
|
||||
822
cocos/bindings/jswrapper/v8/debugger/inspector_agent.cpp
Normal file
822
cocos/bindings/jswrapper/v8/debugger/inspector_agent.cpp
Normal file
@@ -0,0 +1,822 @@
|
||||
#include "inspector_agent.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "env.h"
|
||||
#include "inspector_io.h"
|
||||
#include "node.h"
|
||||
#include "util.h"
|
||||
#include "v8-inspector.h"
|
||||
#include "v8-platform.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, static_cast<int>(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(),
|
||||
static_cast<int>(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(),
|
||||
static_cast<int>(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],
|
||||
static_cast<int>(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) {
|
||||
#if V8_MAJOR_VERSION > 10 || (V8_MAJOR_VERSION == 10 && V8_MINOR_VERSION > 3)
|
||||
session_ = inspector->connect(1, this, StringView(), v8_inspector::V8Inspector::ClientTrustLevel::kFullyTrusted);
|
||||
#else
|
||||
session_ = inspector->connect(1, this, StringView());
|
||||
#endif
|
||||
}
|
||||
|
||||
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) {
|
||||
if (channel_ == nullptr) return;
|
||||
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();
|
||||
|
||||
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(parent_env_->context(), 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"))
|
||||
.Check();
|
||||
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) {
|
||||
if (client_ == nullptr) return;
|
||||
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_->GetForegroundTaskRunner(isolate)->PostTask(std::make_unique<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
|
||||
116
cocos/bindings/jswrapper/v8/debugger/inspector_agent.h
Normal file
116
cocos/bindings/jswrapper/v8/debugger/inspector_agent.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#ifndef SRC_INSPECTOR_AGENT_H_
|
||||
#define SRC_INSPECTOR_AGENT_H_
|
||||
|
||||
#include "../../config.h"
|
||||
#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_
|
||||
533
cocos/bindings/jswrapper/v8/debugger/inspector_io.cpp
Normal file
533
cocos/bindings/jswrapper/v8/debugger/inspector_io.cpp
Normal file
@@ -0,0 +1,533 @@
|
||||
#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/UTF8.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;
|
||||
cc::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;
|
||||
cc::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);
|
||||
}
|
||||
|
||||
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_->GetForegroundTaskRunner(isolate)->PostTask(std::make_unique<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
|
||||
181
cocos/bindings/jswrapper/v8/debugger/inspector_io.h
Normal file
181
cocos/bindings/jswrapper/v8/debugger/inspector_io.h
Normal file
@@ -0,0 +1,181 @@
|
||||
#ifndef SRC_INSPECTOR_IO_H_
|
||||
#define SRC_INSPECTOR_IO_H_
|
||||
|
||||
#include "../../config.h"
|
||||
#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_
|
||||
643
cocos/bindings/jswrapper/v8/debugger/inspector_socket.cpp
Normal file
643
cocos/bindings/jswrapper/v8/debugger/inspector_socket.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
#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], (unsigned int)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 = (int)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, (int)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], (unsigned int)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
|
||||
109
cocos/bindings/jswrapper/v8/debugger/inspector_socket.h
Normal file
109
cocos/bindings/jswrapper/v8/debugger/inspector_socket.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#ifndef SRC_INSPECTOR_SOCKET_H_
|
||||
#define SRC_INSPECTOR_SOCKET_H_
|
||||
|
||||
#include "../../config.h"
|
||||
#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_
|
||||
700
cocos/bindings/jswrapper/v8/debugger/inspector_socket_server.cpp
Normal file
700
cocos/bindings/jswrapper/v8/debugger/inspector_socket_server.cpp
Normal file
@@ -0,0 +1,700 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<std::string, bool, std::string>> ipList;
|
||||
|
||||
{
|
||||
char buf[512];
|
||||
uv_interface_address_t *info = nullptr;
|
||||
int count = 0;
|
||||
int i = 0;
|
||||
|
||||
uv_interface_addresses(&info, &count);
|
||||
i = count;
|
||||
|
||||
if (errno) {
|
||||
SE_LOGE("failed to get addresses %s", strerror(errno));
|
||||
}
|
||||
|
||||
SE_LOGD("Number of interfaces: %d", count);
|
||||
while (i--) {
|
||||
auto &networkInterface = info[i];
|
||||
|
||||
if (networkInterface.address.address4.sin_family == AF_INET) {
|
||||
uv_ip4_name(&networkInterface.address.address4, buf, sizeof(buf));
|
||||
ipList.emplace_back(networkInterface.name, networkInterface.is_internal, buf);
|
||||
}
|
||||
}
|
||||
uv_free_interface_addresses(info, count);
|
||||
}
|
||||
// failed to query device interfaces,
|
||||
if (ipList.empty()) {
|
||||
#if ANDROID
|
||||
SE_LOGD("Please query IP by running the following command in terminal: adb shell ip -4 -br addr");
|
||||
#endif
|
||||
ipList.emplace_back("none", false, "IP_ADDR_OF_THIS_DEVICE");
|
||||
}
|
||||
|
||||
for (const std::string &id : ids) {
|
||||
if (host != "0.0.0.0") {
|
||||
SE_LOGD("Debugger listening..., visit [ devtools://devtools/bundled/js_app.html?v8only=true&ws=%s ] in chrome browser to debug!",
|
||||
FormatWsAddress(host, port, id, false).c_str());
|
||||
} else {
|
||||
SE_LOGD("Debugger listening..., visit [");
|
||||
for (auto &nif : ipList) {
|
||||
SE_LOGD(" devtools://devtools/bundled/js_app.html?v8only=true&ws=%s",
|
||||
FormatWsAddress(std::get<2>(nif), port, id, false).c_str());
|
||||
}
|
||||
SE_LOGD(" ] in chrome browser to debug!");
|
||||
}
|
||||
}
|
||||
SE_LOGD("For help see %s",
|
||||
"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"] = "Cocos 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 = static_cast<uInt>(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
|
||||
105
cocos/bindings/jswrapper/v8/debugger/inspector_socket_server.h
Normal file
105
cocos/bindings/jswrapper/v8/debugger/inspector_socket_server.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#ifndef SRC_INSPECTOR_SOCKET_SERVER_H_
|
||||
#define SRC_INSPECTOR_SOCKET_SERVER_H_
|
||||
|
||||
#include "../../config.h"
|
||||
#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_
|
||||
1118
cocos/bindings/jswrapper/v8/debugger/node.cpp
Normal file
1118
cocos/bindings/jswrapper/v8/debugger/node.cpp
Normal file
File diff suppressed because it is too large
Load Diff
160
cocos/bindings/jswrapper/v8/debugger/node.h
Normal file
160
cocos/bindings/jswrapper/v8/debugger/node.h
Normal file
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../config.h"
|
||||
#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
|
||||
141
cocos/bindings/jswrapper/v8/debugger/node_debug_options.cpp
Normal file
141
cocos/bindings/jswrapper/v8/debugger/node_debug_options.cpp
Normal file
@@ -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
|
||||
45
cocos/bindings/jswrapper/v8/debugger/node_debug_options.h
Normal file
45
cocos/bindings/jswrapper/v8/debugger/node_debug_options.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef SRC_NODE_DEBUG_OPTIONS_H_
|
||||
#define SRC_NODE_DEBUG_OPTIONS_H_
|
||||
|
||||
#include "../../config.h"
|
||||
#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_
|
||||
196
cocos/bindings/jswrapper/v8/debugger/node_mutex.h
Normal file
196
cocos/bindings/jswrapper/v8/debugger/node_mutex.h
Normal file
@@ -0,0 +1,196 @@
|
||||
#ifndef SRC_NODE_MUTEX_H_
|
||||
#define SRC_NODE_MUTEX_H_
|
||||
|
||||
#include "../../config.h"
|
||||
#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_
|
||||
432
cocos/bindings/jswrapper/v8/debugger/util-inl.h
Normal file
432
cocos/bindings/jswrapper/v8/debugger/util-inl.h
Normal file
@@ -0,0 +1,432 @@
|
||||
// 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_
|
||||
117
cocos/bindings/jswrapper/v8/debugger/util.cpp
Normal file
117
cocos/bindings/jswrapper/v8/debugger/util.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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
|
||||
449
cocos/bindings/jswrapper/v8/debugger/util.h
Normal file
449
cocos/bindings/jswrapper/v8/debugger/util.h
Normal file
@@ -0,0 +1,449 @@
|
||||
// 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.h"
|
||||
#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