JavaScript-Duktape

 view release on metacpan or  search on metacpan

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

 */

#if !defined(DUK_INTERNAL_H_INCLUDED)
#define DUK_INTERNAL_H_INCLUDED

/*
 *  The 'duktape.h' header provides the public API, but also handles all
 *  compiler and platform specific feature detection, Duktape feature
 *  resolution, inclusion of system headers, etc.  These have been merged
 *  because the public API is also dependent on e.g. detecting appropriate
 *  C types which is quite platform/compiler specific especially for a non-C99
 *  build.  The public API is also dependent on the resolved feature set.
 *
 *  Some actions taken by the merged header (such as including system headers)
 *  are not appropriate for building a user application.  The define
 *  DUK_COMPILING_DUKTAPE allows the merged header to skip/include some
 *  sections depending on what is being built.
 */

#define DUK_COMPILING_DUKTAPE
#include "duktape.h"

/*
 *  User declarations, e.g. prototypes for user functions used by Duktape
 *  macros.
 */

DUK_USE_USER_DECLARE()

/*
 *  Duktape includes (other than duk_features.h)
 *
 *  The header files expect to be included in an order which satisfies header
 *  dependencies correctly (the headers themselves don't include any other
 *  includes).  Forward declarations are used to break circular struct/typedef
 *  dependencies.
 */

/* #include duk_dblunion.h */
#line 1 "duk_dblunion.h"
/*
 *  Union to access IEEE double memory representation, indexes for double
 *  memory representation, and some macros for double manipulation.
 *
 *  Also used by packed duk_tval.  Use a union for bit manipulation to
 *  minimize aliasing issues in practice.  The C99 standard does not
 *  guarantee that this should work, but it's a very widely supported
 *  practice for low level manipulation.
 *
 *  IEEE double format summary:
 *
 *    seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
 *       A        B        C        D        E        F        G        H
 *
 *    s       sign bit
 *    eee...  exponent field
 *    fff...  fraction
 *
 *  See http://en.wikipedia.org/wiki/Double_precision_floating-point_format.
 *
 *  NaNs are represented as exponent 0x7ff and mantissa != 0.  The NaN is a
 *  signaling NaN when the highest bit of the mantissa is zero, and a quiet
 *  NaN when the highest bit is set.
 *
 *  At least three memory layouts are relevant here:
 *
 *    A B C D E F G H    Big endian (e.g. 68k)           DUK_USE_DOUBLE_BE
 *    H G F E D C B A    Little endian (e.g. x86)        DUK_USE_DOUBLE_LE
 *    D C B A H G F E    Mixed/cross endian (e.g. ARM)   DUK_USE_DOUBLE_ME
 *
 *  ARM is a special case: ARM double values are in mixed/cross endian
 *  format while ARM duk_uint64_t values are in standard little endian
 *  format (H G F E D C B A).  When a double is read as a duk_uint64_t
 *  from memory, the register will contain the (logical) value
 *  E F G H A B C D.  This requires some special handling below.
 *
 *  Indexes of various types (8-bit, 16-bit, 32-bit) in memory relative to
 *  the logical (big endian) order:
 *
 *  byte order      duk_uint8_t    duk_uint16_t     duk_uint32_t
 *    BE             01234567         0123               01
 *    LE             76543210         3210               10
 *    ME (ARM)       32107654         1032               01
 *
 *  Some processors may alter NaN values in a floating point load+store.
 *  For instance, on X86 a FLD + FSTP may convert a signaling NaN to a
 *  quiet one.  This is catastrophic when NaN space is used in packed
 *  duk_tval values.  See: misc/clang_aliasing.c.
 */

#if !defined(DUK_DBLUNION_H_INCLUDED)
#define DUK_DBLUNION_H_INCLUDED

/*
 *  Union for accessing double parts, also serves as packed duk_tval
 */

union duk_double_union {
	double d;
	float f[2];
#if defined(DUK_USE_64BIT_OPS)
	duk_uint64_t ull[1];
#endif
	duk_uint32_t ui[2];
	duk_uint16_t us[4];
	duk_uint8_t uc[8];
#if defined(DUK_USE_PACKED_TVAL)
	void *vp[2];  /* used by packed duk_tval, assumes sizeof(void *) == 4 */
#endif
};

typedef union duk_double_union duk_double_union;

/*
 *  Indexes of various types with respect to big endian (logical) layout
 */

#if defined(DUK_USE_DOUBLE_LE)
#if defined(DUK_USE_64BIT_OPS)
#define DUK_DBL_IDX_ULL0   0
#endif
#define DUK_DBL_IDX_UI0    1
#define DUK_DBL_IDX_UI1    0
#define DUK_DBL_IDX_US0    3
#define DUK_DBL_IDX_US1    2
#define DUK_DBL_IDX_US2    1
#define DUK_DBL_IDX_US3    0
#define DUK_DBL_IDX_UC0    7
#define DUK_DBL_IDX_UC1    6
#define DUK_DBL_IDX_UC2    5
#define DUK_DBL_IDX_UC3    4
#define DUK_DBL_IDX_UC4    3
#define DUK_DBL_IDX_UC5    2
#define DUK_DBL_IDX_UC6    1
#define DUK_DBL_IDX_UC7    0
#define DUK_DBL_IDX_VP0    DUK_DBL_IDX_UI0  /* packed tval */
#define DUK_DBL_IDX_VP1    DUK_DBL_IDX_UI1  /* packed tval */
#elif defined(DUK_USE_DOUBLE_BE)
#if defined(DUK_USE_64BIT_OPS)
#define DUK_DBL_IDX_ULL0   0
#endif
#define DUK_DBL_IDX_UI0    0
#define DUK_DBL_IDX_UI1    1
#define DUK_DBL_IDX_US0    0
#define DUK_DBL_IDX_US1    1
#define DUK_DBL_IDX_US2    2
#define DUK_DBL_IDX_US3    3

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

#endif

/*
 *  Helper macros for reading/writing memory representation parts, used
 *  by duk_numconv.c and duk_tval.h.
 */

#define DUK_DBLUNION_SET_DOUBLE(u,v)  do {  \
		(u)->d = (v); \
	} while (0)

#define DUK_DBLUNION_SET_HIGH32(u,v)  do {  \
		(u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \
	} while (0)

#if defined(DUK_USE_64BIT_OPS)
#if defined(DUK_USE_DOUBLE_ME)
#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v)  do { \
		(u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \
	} while (0)
#else
#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v)  do { \
		(u)->ull[DUK_DBL_IDX_ULL0] = ((duk_uint64_t) (v)) << 32; \
	} while (0)
#endif
#else  /* DUK_USE_64BIT_OPS */
#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v)  do { \
		(u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \
		(u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0; \
	} while (0)
#endif  /* DUK_USE_64BIT_OPS */

#define DUK_DBLUNION_SET_LOW32(u,v)  do {  \
		(u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \
	} while (0)

#define DUK_DBLUNION_GET_DOUBLE(u)  ((u)->d)
#define DUK_DBLUNION_GET_HIGH32(u)  ((u)->ui[DUK_DBL_IDX_UI0])
#define DUK_DBLUNION_GET_LOW32(u)   ((u)->ui[DUK_DBL_IDX_UI1])

#if defined(DUK_USE_64BIT_OPS)
#if defined(DUK_USE_DOUBLE_ME)
#define DUK_DBLUNION_SET_UINT64(u,v)  do { \
		(u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) ((v) >> 32); \
		(u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \
	} while (0)
#define DUK_DBLUNION_GET_UINT64(u) \
	((((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI0]) << 32) | \
	 ((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI1]))
#else
#define DUK_DBLUNION_SET_UINT64(u,v)  do { \
		(u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \
	} while (0)
#define DUK_DBLUNION_GET_UINT64(u)  ((u)->ull[DUK_DBL_IDX_ULL0])
#endif
#define DUK_DBLUNION_SET_INT64(u,v) DUK_DBLUNION_SET_UINT64((u), (duk_uint64_t) (v))
#define DUK_DBLUNION_GET_INT64(u)   ((duk_int64_t) DUK_DBLUNION_GET_UINT64((u)))
#endif  /* DUK_USE_64BIT_OPS */

/*
 *  Double NaN manipulation macros related to NaN normalization needed when
 *  using the packed duk_tval representation.  NaN normalization is necessary
 *  to keep double values compatible with the duk_tval format.
 *
 *  When packed duk_tval is used, the NaN space is used to store pointers
 *  and other tagged values in addition to NaNs.  Actual NaNs are normalized
 *  to a specific quiet NaN.  The macros below are used by the implementation
 *  to check and normalize NaN values when they might be created.  The macros
 *  are essentially NOPs when the non-packed duk_tval representation is used.
 *
 *  A FULL check is exact and checks all bits.  A NOTFULL check is used by
 *  the packed duk_tval and works correctly for all NaNs except those that
 *  begin with 0x7ff0.  Since the 'normalized NaN' values used with packed
 *  duk_tval begin with 0x7ff8, the partial check is reliable when packed
 *  duk_tval is used.  The 0x7ff8 prefix means the normalized NaN will be a
 *  quiet NaN regardless of its remaining lower bits.
 *
 *  The ME variant below is specifically for ARM byte order, which has the
 *  feature that while doubles have a mixed byte order (32107654), unsigned
 *  long long values has a little endian byte order (76543210).  When writing
 *  a logical double value through a ULL pointer, the 32-bit words need to be
 *  swapped; hence the #if defined()s below for ULL writes with DUK_USE_DOUBLE_ME.
 *  This is not full ARM support but suffices for some environments.
 */

#if defined(DUK_USE_64BIT_OPS)
#if defined(DUK_USE_DOUBLE_ME)
/* Macros for 64-bit ops + mixed endian doubles. */
#define DUK__DBLUNION_SET_NAN_FULL(u)  do { \
		(u)->ull[DUK_DBL_IDX_ULL0] = DUK_U64_CONSTANT(0x000000007ff80000); \
	} while (0)
#define DUK__DBLUNION_IS_NAN_FULL(u) \
	((((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000)) && \
	 ((((u)->ull[DUK_DBL_IDX_ULL0]) & DUK_U64_CONSTANT(0xffffffff000fffff)) != 0))
#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x000000007ff80000))
#define DUK__DBLUNION_IS_ANYINF(u) \
	(((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0xffffffff7fffffff)) == DUK_U64_CONSTANT(0x000000007ff00000))
#define DUK__DBLUNION_IS_POSINF(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x000000007ff00000))
#define DUK__DBLUNION_IS_NEGINF(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x00000000fff00000))
#define DUK__DBLUNION_IS_ANYZERO(u) \
	(((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0xffffffff7fffffff)) == DUK_U64_CONSTANT(0x0000000000000000))
#define DUK__DBLUNION_IS_POSZERO(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000000000000))
#define DUK__DBLUNION_IS_NEGZERO(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000080000000))
#else
/* Macros for 64-bit ops + big/little endian doubles. */
#define DUK__DBLUNION_SET_NAN_FULL(u)  do { \
		(u)->ull[DUK_DBL_IDX_ULL0] = DUK_U64_CONSTANT(0x7ff8000000000000); \
	} while (0)
#define DUK__DBLUNION_IS_NAN_FULL(u) \
	((((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000)) == DUK_U64_CONSTANT(0x7ff0000000000000)) && \
	 ((((u)->ull[DUK_DBL_IDX_ULL0]) & DUK_U64_CONSTANT(0x000fffffffffffff)) != 0))
#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x7ff8000000000000))
#define DUK__DBLUNION_IS_ANYINF(u) \
	(((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7fffffffffffffff)) == DUK_U64_CONSTANT(0x7ff0000000000000))
#define DUK__DBLUNION_IS_POSINF(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x7ff0000000000000))
#define DUK__DBLUNION_IS_NEGINF(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0xfff0000000000000))
#define DUK__DBLUNION_IS_ANYZERO(u) \
	(((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7fffffffffffffff)) == DUK_U64_CONSTANT(0x0000000000000000))
#define DUK__DBLUNION_IS_POSZERO(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000000000000))
#define DUK__DBLUNION_IS_NEGZERO(u) \
	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x8000000000000000))
#endif
#else  /* DUK_USE_64BIT_OPS */
/* Macros for no 64-bit ops, any endianness. */
#define DUK__DBLUNION_SET_NAN_FULL(u)  do { \
		(u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) 0x7ff80000UL; \
		(u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0x00000000UL; \
	} while (0)
#define DUK__DBLUNION_IS_NAN_FULL(u) \
	((((u)->ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL) && \
	 (((u)->ui[DUK_DBL_IDX_UI0] & 0x000fffffUL) != 0 || \
          (u)->ui[DUK_DBL_IDX_UI1] != 0))
#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \
	(((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff80000UL) && \
	 ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_ANYINF(u) \
	((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x7ff00000UL) && \
	 ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_POSINF(u) \
	(((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff00000UL) && \
	 ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_NEGINF(u) \
	(((u)->ui[DUK_DBL_IDX_UI0] == 0xfff00000UL) && \
	 ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_ANYZERO(u) \
	((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x00000000UL) && \
	 ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_POSZERO(u) \
	(((u)->ui[DUK_DBL_IDX_UI0] == 0x00000000UL) && \
	 ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#define DUK__DBLUNION_IS_NEGZERO(u) \
	(((u)->ui[DUK_DBL_IDX_UI0] == 0x80000000UL) && \
	 ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL))
#endif  /* DUK_USE_64BIT_OPS */

#define DUK__DBLUNION_SET_NAN_NOTFULL(u)  do { \
		(u)->us[DUK_DBL_IDX_US0] = 0x7ff8UL; \
	} while (0)

#define DUK__DBLUNION_IS_NAN_NOTFULL(u) \
	/* E == 0x7ff, topmost four bits of F != 0 => assume NaN */ \
	((((u)->us[DUK_DBL_IDX_US0] & 0x7ff0UL) == 0x7ff0UL) && \
	 (((u)->us[DUK_DBL_IDX_US0] & 0x000fUL) != 0x0000UL))

#define DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL(u) \
	/* E == 0x7ff, F == 8 => normalized NaN */ \
	((u)->us[DUK_DBL_IDX_US0] == 0x7ff8UL)

#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL(u)  do { \
		if (DUK__DBLUNION_IS_NAN_FULL((u))) { \
			DUK__DBLUNION_SET_NAN_FULL((u)); \
		} \
	} while (0)

#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL(u)  do { \
		if (DUK__DBLUNION_IS_NAN_NOTFULL((u))) { \
			DUK__DBLUNION_SET_NAN_NOTFULL((u)); \
		} \
	} while (0)

/* Concrete macros for NaN handling used by the implementation internals.
 * Chosen so that they match the duk_tval representation: with a packed
 * duk_tval, ensure NaNs are properly normalized; with a non-packed duk_tval
 * these are essentially NOPs.
 */

#if defined(DUK_USE_PACKED_TVAL)
#if defined(DUK_USE_FULL_TVAL)
#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u)  DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL((u))
#define DUK_DBLUNION_IS_NAN(u)               DUK__DBLUNION_IS_NAN_FULL((u))
#define DUK_DBLUNION_IS_NORMALIZED_NAN(u)    DUK__DBLUNION_IS_NORMALIZED_NAN_FULL((u))
#define DUK_DBLUNION_SET_NAN(d)              DUK__DBLUNION_SET_NAN_FULL((d))
#else
#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u)  DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL((u))
#define DUK_DBLUNION_IS_NAN(u)               DUK__DBLUNION_IS_NAN_NOTFULL((u))
#define DUK_DBLUNION_IS_NORMALIZED_NAN(u)    DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL((u))
#define DUK_DBLUNION_SET_NAN(d)              DUK__DBLUNION_SET_NAN_NOTFULL((d))
#endif
#define DUK_DBLUNION_IS_NORMALIZED(u) \
	(!DUK_DBLUNION_IS_NAN((u)) ||  /* either not a NaN */ \
	 DUK_DBLUNION_IS_NORMALIZED_NAN((u)))  /* or is a normalized NaN */
#else  /* DUK_USE_PACKED_TVAL */
#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u)  /* nop: no need to normalize */
#define DUK_DBLUNION_IS_NAN(u)               DUK__DBLUNION_IS_NAN_FULL((u))  /* (DUK_ISNAN((u)->d)) */
#define DUK_DBLUNION_IS_NORMALIZED_NAN(u)    DUK__DBLUNION_IS_NAN_FULL((u))  /* (DUK_ISNAN((u)->d)) */
#define DUK_DBLUNION_IS_NORMALIZED(u)        1  /* all doubles are considered normalized */
#define DUK_DBLUNION_SET_NAN(u)  do { \
		/* in non-packed representation we don't care about which NaN is used */ \
		(u)->d = DUK_DOUBLE_NAN; \
	} while (0)
#endif  /* DUK_USE_PACKED_TVAL */

#define DUK_DBLUNION_IS_ANYINF(u) DUK__DBLUNION_IS_ANYINF((u))
#define DUK_DBLUNION_IS_POSINF(u) DUK__DBLUNION_IS_POSINF((u))
#define DUK_DBLUNION_IS_NEGINF(u) DUK__DBLUNION_IS_NEGINF((u))

#define DUK_DBLUNION_IS_ANYZERO(u) DUK__DBLUNION_IS_ANYZERO((u))
#define DUK_DBLUNION_IS_POSZERO(u) DUK__DBLUNION_IS_POSZERO((u))
#define DUK_DBLUNION_IS_NEGZERO(u) DUK__DBLUNION_IS_NEGZERO((u))

/* XXX: native 64-bit byteswaps when available */

/* 64-bit byteswap, same operation independent of target endianness. */
#define DUK_DBLUNION_BSWAP64(u) do { \
		duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \
		duk__bswaptmp1 = (u)->ui[0]; \
		duk__bswaptmp2 = (u)->ui[1]; \
		duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \
		duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \
		(u)->ui[0] = duk__bswaptmp2; \
		(u)->ui[1] = duk__bswaptmp1; \
	} while (0)

/* Byteswap an IEEE double in the duk_double_union from host to network
 * order.  For a big endian target this is a no-op.
 */
#if defined(DUK_USE_DOUBLE_LE)
#define DUK_DBLUNION_DOUBLE_HTON(u) do { \
		duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \
		duk__bswaptmp1 = (u)->ui[0]; \
		duk__bswaptmp2 = (u)->ui[1]; \
		duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \
		duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \
		(u)->ui[0] = duk__bswaptmp2; \
		(u)->ui[1] = duk__bswaptmp1; \
	} while (0)
#elif defined(DUK_USE_DOUBLE_ME)
#define DUK_DBLUNION_DOUBLE_HTON(u) do { \
		duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \
		duk__bswaptmp1 = (u)->ui[0]; \
		duk__bswaptmp2 = (u)->ui[1]; \
		duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \
		duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \
		(u)->ui[0] = duk__bswaptmp1; \
		(u)->ui[1] = duk__bswaptmp2; \
	} while (0)
#elif defined(DUK_USE_DOUBLE_BE)
#define DUK_DBLUNION_DOUBLE_HTON(u) do { } while (0)
#else
#error internal error, double endianness insane
#endif

/* Reverse operation is the same. */
#define DUK_DBLUNION_DOUBLE_NTOH(u) DUK_DBLUNION_DOUBLE_HTON((u))

/* Some sign bit helpers. */
#if defined(DUK_USE_64BIT_OPS)
#define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x8000000000000000)) != 0)

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN


typedef struct duk_token duk_token;
typedef struct duk_re_token duk_re_token;
typedef struct duk_lexer_point duk_lexer_point;
typedef struct duk_lexer_ctx duk_lexer_ctx;
typedef struct duk_lexer_codepoint duk_lexer_codepoint;

typedef struct duk_compiler_instr duk_compiler_instr;
typedef struct duk_compiler_func duk_compiler_func;
typedef struct duk_compiler_ctx duk_compiler_ctx;

typedef struct duk_re_matcher_ctx duk_re_matcher_ctx;
typedef struct duk_re_compiler_ctx duk_re_compiler_ctx;

#endif  /* DUK_FORWDECL_H_INCLUDED */
/* #include duk_tval.h */
#line 1 "duk_tval.h"
/*
 *  Tagged type definition (duk_tval) and accessor macros.
 *
 *  Access all fields through the accessor macros, as the representation
 *  is quite tricky.
 *
 *  There are two packed type alternatives: an 8-byte representation
 *  based on an IEEE double (preferred for compactness), and a 12-byte
 *  representation (portability).  The latter is needed also in e.g.
 *  64-bit environments (it usually pads to 16 bytes per value).
 *
 *  Selecting the tagged type format involves many trade-offs (memory
 *  use, size and performance of generated code, portability, etc).
 *
 *  NB: because macro arguments are often expressions, macros should
 *  avoid evaluating their argument more than once.
 */

#if !defined(DUK_TVAL_H_INCLUDED)
#define DUK_TVAL_H_INCLUDED

/* sanity */
#if !defined(DUK_USE_DOUBLE_LE) && !defined(DUK_USE_DOUBLE_ME) && !defined(DUK_USE_DOUBLE_BE)
#error unsupported: cannot determine byte order variant
#endif

#if defined(DUK_USE_PACKED_TVAL)
/* ======================================================================== */

/*
 *  Packed 8-byte representation
 */

/* use duk_double_union as duk_tval directly */
typedef union duk_double_union duk_tval;
typedef struct {
	duk_uint16_t a;
	duk_uint16_t b;
	duk_uint16_t c;
	duk_uint16_t d;
} duk_tval_unused;

/* tags */
#define DUK_TAG_NORMALIZED_NAN    0x7ff8UL   /* the NaN variant we use */
/* avoid tag 0xfff0, no risk of confusion with negative infinity */
#define DUK_TAG_MIN               0xfff1UL
#if defined(DUK_USE_FASTINT)
#define DUK_TAG_FASTINT           0xfff1UL   /* embed: integer value */
#endif
#define DUK_TAG_UNUSED            0xfff2UL   /* marker; not actual tagged value */
#define DUK_TAG_UNDEFINED         0xfff3UL   /* embed: nothing */
#define DUK_TAG_NULL              0xfff4UL   /* embed: nothing */
#define DUK_TAG_BOOLEAN           0xfff5UL   /* embed: 0 or 1 (false or true) */
/* DUK_TAG_NUMBER would logically go here, but it has multiple 'tags' */
#define DUK_TAG_POINTER           0xfff6UL   /* embed: void ptr */
#define DUK_TAG_LIGHTFUNC         0xfff7UL   /* embed: func ptr */
#define DUK_TAG_STRING            0xfff8UL   /* embed: duk_hstring ptr */
#define DUK_TAG_OBJECT            0xfff9UL   /* embed: duk_hobject ptr */
#define DUK_TAG_BUFFER            0xfffaUL   /* embed: duk_hbuffer ptr */
#define DUK_TAG_MAX               0xfffaUL

/* for convenience */
#define DUK_XTAG_BOOLEAN_FALSE    0xfff50000UL
#define DUK_XTAG_BOOLEAN_TRUE     0xfff50001UL

#define DUK_TVAL_IS_VALID_TAG(tv) \
	(DUK_TVAL_GET_TAG((tv)) - DUK_TAG_MIN <= DUK_TAG_MAX - DUK_TAG_MIN)

/* DUK_TVAL_UNUSED initializer for duk_tval_unused, works for any endianness. */
#define DUK_TVAL_UNUSED_INITIALIZER() \
	{ DUK_TAG_UNUSED, DUK_TAG_UNUSED, DUK_TAG_UNUSED, DUK_TAG_UNUSED }

/* two casts to avoid gcc warning: "warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]" */
#if defined(DUK_USE_64BIT_OPS)
#if defined(DUK_USE_DOUBLE_ME)
#define DUK__TVAL_SET_TAGGEDPOINTER(tv,h,tag)  do { \
		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) (tag)) << 16) | (((duk_uint64_t) (duk_uint32_t) (h)) << 32); \
	} while (0)
#else
#define DUK__TVAL_SET_TAGGEDPOINTER(tv,h,tag)  do { \
		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) (tag)) << 48) | ((duk_uint64_t) (duk_uint32_t) (h)); \
	} while (0)
#endif
#else  /* DUK_USE_64BIT_OPS */
#define DUK__TVAL_SET_TAGGEDPOINTER(tv,h,tag)  do { \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) (tag)) << 16; \
		duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (h); \
	} while (0)
#endif  /* DUK_USE_64BIT_OPS */

#if defined(DUK_USE_64BIT_OPS)
/* Double casting for pointer to avoid gcc warning (cast from pointer to integer of different size) */
#if defined(DUK_USE_DOUBLE_ME)
#define DUK__TVAL_SET_LIGHTFUNC(tv,fp,flags)  do { \
		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 16) | \
		                              ((duk_uint64_t) (flags)) | \
		                              (((duk_uint64_t) (duk_uint32_t) (fp)) << 32); \
	} while (0)
#else
#define DUK__TVAL_SET_LIGHTFUNC(tv,fp,flags)  do { \
		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 48) | \
		                              (((duk_uint64_t) (flags)) << 32) | \

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

		duk__tv = (tv); \
		duk__tv->ui[DUK_DBL_IDX_UI0] = (((duk_uint32_t) DUK_TAG_LIGHTFUNC) << 16) | ((duk_uint32_t) (flags)); \
		duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (fp); \
	} while (0)
#endif  /* DUK_USE_64BIT_OPS */

#if defined(DUK_USE_FASTINT)
/* Note: masking is done for 'i' to deal with negative numbers correctly */
#if defined(DUK_USE_DOUBLE_ME)
#define DUK__TVAL_SET_I48(tv,i)  do { \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) DUK_TAG_FASTINT) << 16 | (((duk_uint32_t) ((i) >> 32)) & 0x0000ffffUL); \
		duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (i); \
	} while (0)
#define DUK__TVAL_SET_U32(tv,i)  do { \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) DUK_TAG_FASTINT) << 16; \
		duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (i); \
	} while (0)
#else
#define DUK__TVAL_SET_I48(tv,i)  do { \
		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (((duk_uint64_t) (i)) & DUK_U64_CONSTANT(0x0000ffffffffffff)); \
	} while (0)
#define DUK__TVAL_SET_U32(tv,i)  do { \
		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (duk_uint64_t) (i); \
	} while (0)
#endif

/* This needs to go through a cast because sign extension is needed. */
#define DUK__TVAL_SET_I32(tv,i)  do { \
		duk_int64_t duk__tmp = (duk_int64_t) (i); \
		DUK_TVAL_SET_I48((tv), duk__tmp); \
	} while (0)

/* XXX: Clumsy sign extend and masking of 16 topmost bits. */
#if defined(DUK_USE_DOUBLE_ME)
#define DUK__TVAL_GET_FASTINT(tv)      (((duk_int64_t) ((((duk_uint64_t) (tv)->ui[DUK_DBL_IDX_UI0]) << 32) | ((duk_uint64_t) (tv)->ui[DUK_DBL_IDX_UI1]))) << 16 >> 16)
#else
#define DUK__TVAL_GET_FASTINT(tv)      ((((duk_int64_t) (tv)->ull[DUK_DBL_IDX_ULL0]) << 16) >> 16)
#endif
#define DUK__TVAL_GET_FASTINT_U32(tv)  ((tv)->ui[DUK_DBL_IDX_UI1])
#define DUK__TVAL_GET_FASTINT_I32(tv)  ((duk_int32_t) (tv)->ui[DUK_DBL_IDX_UI1])
#endif  /* DUK_USE_FASTINT */

#define DUK_TVAL_SET_UNDEFINED(tv)  do { \
		(tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_UNDEFINED; \
	} while (0)
#define DUK_TVAL_SET_UNUSED(tv)  do { \
		(tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_UNUSED; \
	} while (0)
#define DUK_TVAL_SET_NULL(tv)  do { \
		(tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_NULL; \
	} while (0)

#define DUK_TVAL_SET_BOOLEAN(tv,val)         DUK_DBLUNION_SET_HIGH32((tv), (((duk_uint32_t) DUK_TAG_BOOLEAN) << 16) | ((duk_uint32_t) (val)))

#define DUK_TVAL_SET_NAN(tv)                 DUK_DBLUNION_SET_NAN_FULL((tv))

/* Assumes that caller has normalized NaNs, otherwise trouble ahead. */
#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_SET_DOUBLE(tv,d)  do { \
		duk_double_t duk__dblval; \
		duk__dblval = (d); \
		DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); \
		DUK_DBLUNION_SET_DOUBLE((tv), duk__dblval); \
	} while (0)
#define DUK_TVAL_SET_I48(tv,i)               DUK__TVAL_SET_I48((tv), (i))
#define DUK_TVAL_SET_I32(tv,i)               DUK__TVAL_SET_I32((tv), (i))
#define DUK_TVAL_SET_U32(tv,i)               DUK__TVAL_SET_U32((tv), (i))
#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv,d)  duk_tval_set_number_chkfast_fast((tv), (d))
#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv,d)  duk_tval_set_number_chkfast_slow((tv), (d))
#define DUK_TVAL_SET_NUMBER(tv,d)            DUK_TVAL_SET_DOUBLE((tv), (d))
#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv)  do { \
		duk_tval *duk__tv; \
		duk_double_t duk__d; \
		duk__tv = (tv); \
		if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \
			duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \
			DUK_TVAL_SET_NUMBER_CHKFAST_FAST(duk__tv, duk__d); \
		} \
	} while (0)
#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv)  do { \
		duk_tval *duk__tv; \
		duk_double_t duk__d; \
		duk__tv = (tv); \
		if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \
			duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \
			DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(duk__tv, duk__d); \
		} \
	} while (0)
#else  /* DUK_USE_FASTINT */
#define DUK_TVAL_SET_DOUBLE(tv,d)  do { \
		duk_double_t duk__dblval; \
		duk__dblval = (d); \
		DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); \
		DUK_DBLUNION_SET_DOUBLE((tv), duk__dblval); \
	} while (0)
#define DUK_TVAL_SET_I48(tv,i)               DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i))  /* XXX: fast int-to-double */
#define DUK_TVAL_SET_I32(tv,i)               DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i))
#define DUK_TVAL_SET_U32(tv,i)               DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i))
#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv,d)    DUK_TVAL_SET_DOUBLE((tv), (d))
#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv,d)    DUK_TVAL_SET_DOUBLE((tv), (d))
#define DUK_TVAL_SET_NUMBER(tv,d)            DUK_TVAL_SET_DOUBLE((tv), (d))
#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv)  do { } while (0)
#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv)  do { } while (0)
#endif  /* DUK_USE_FASTINT */

#define DUK_TVAL_SET_FASTINT(tv,i)           DUK_TVAL_SET_I48((tv), (i))  /* alias */

#define DUK_TVAL_SET_LIGHTFUNC(tv,fp,flags)  DUK__TVAL_SET_LIGHTFUNC((tv), (fp), (flags))
#define DUK_TVAL_SET_STRING(tv,h)            DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_STRING)
#define DUK_TVAL_SET_OBJECT(tv,h)            DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_OBJECT)
#define DUK_TVAL_SET_BUFFER(tv,h)            DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_BUFFER)
#define DUK_TVAL_SET_POINTER(tv,p)           DUK__TVAL_SET_TAGGEDPOINTER((tv), (p), DUK_TAG_POINTER)

#define DUK_TVAL_SET_TVAL(tv,x)              do { *(tv) = *(x); } while (0)

/* getters */
#define DUK_TVAL_GET_BOOLEAN(tv)             ((duk_small_uint_t) (tv)->us[DUK_DBL_IDX_US1])

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val))
#define DUK_TVAL_SET_I32(tv,val) \
	DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val))
#define DUK_TVAL_SET_NUMBER(tv,val)  do { \
		duk_tval *duk__tv; \
		duk_double_t duk__dblval; \
		duk__dblval = (val); \
		DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); /* nop for unpacked duk_tval */ \
		duk__tv = (tv); \
		duk__tv->t = DUK_TAG_NUMBER; \
		duk__tv->v.d = duk__dblval; \
	} while (0)
#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv,d) \
	DUK_TVAL_SET_NUMBER((tv), (d))
#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv,d) \
	DUK_TVAL_SET_NUMBER((tv), (d))
#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv)  do { } while (0)
#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv)  do { } while (0)
#endif  /* DUK_USE_FASTINT */

#define DUK_TVAL_SET_FASTINT(tv,i) \
	DUK_TVAL_SET_I48((tv), (i))  /* alias */

#define DUK_TVAL_SET_POINTER(tv,hptr)  do { \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->t = DUK_TAG_POINTER; \
		duk__tv->v.voidptr = (hptr); \
	} while (0)

#define DUK_TVAL_SET_LIGHTFUNC(tv,fp,flags)  do { \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->t = DUK_TAG_LIGHTFUNC; \
		duk__tv->v_extra = (flags); \
		duk__tv->v.lightfunc = (duk_c_function) (fp); \
	} while (0)

#define DUK_TVAL_SET_STRING(tv,hptr)  do { \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->t = DUK_TAG_STRING; \
		duk__tv->v.hstring = (hptr); \
	} while (0)

#define DUK_TVAL_SET_OBJECT(tv,hptr)  do { \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->t = DUK_TAG_OBJECT; \
		duk__tv->v.hobject = (hptr); \
	} while (0)

#define DUK_TVAL_SET_BUFFER(tv,hptr)  do { \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->t = DUK_TAG_BUFFER; \
		duk__tv->v.hbuffer = (hptr); \
	} while (0)

#define DUK_TVAL_SET_NAN(tv)  do { \
		/* in non-packed representation we don't care about which NaN is used */ \
		duk_tval *duk__tv; \
		duk__tv = (tv); \
		duk__tv->t = DUK_TAG_NUMBER; \
		duk__tv->v.d = DUK_DOUBLE_NAN; \
	} while (0)

#define DUK_TVAL_SET_TVAL(tv,x)            do { *(tv) = *(x); } while (0)

/* getters */
#define DUK_TVAL_GET_BOOLEAN(tv)           ((duk_small_uint_t) (tv)->v.i)
#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_GET_DOUBLE(tv)            ((tv)->v.d)
#define DUK_TVAL_GET_FASTINT(tv)           ((tv)->v.fi)
#define DUK_TVAL_GET_FASTINT_U32(tv)       ((duk_uint32_t) ((tv)->v.fi))
#define DUK_TVAL_GET_FASTINT_I32(tv)       ((duk_int32_t) ((tv)->v.fi))
#if 0
#define DUK_TVAL_GET_NUMBER(tv)            (DUK_TVAL_IS_FASTINT((tv)) ? \
                                               (duk_double_t) DUK_TVAL_GET_FASTINT((tv)) : \
                                               DUK_TVAL_GET_DOUBLE((tv)))
#define DUK_TVAL_GET_NUMBER(tv)            duk_tval_get_number_unpacked((tv))
#else
/* This seems reasonable overall. */
#define DUK_TVAL_GET_NUMBER(tv)            (DUK_TVAL_IS_FASTINT((tv)) ? \
                                               duk_tval_get_number_unpacked_fastint((tv)) : \
                                               DUK_TVAL_GET_DOUBLE((tv)))
#endif
#else
#define DUK_TVAL_GET_NUMBER(tv)            ((tv)->v.d)
#define DUK_TVAL_GET_DOUBLE(tv)            ((tv)->v.d)
#endif  /* DUK_USE_FASTINT */
#define DUK_TVAL_GET_POINTER(tv)           ((tv)->v.voidptr)
#define DUK_TVAL_GET_LIGHTFUNC(tv,out_fp,out_flags)  do { \
		(out_flags) = (duk_uint32_t) (tv)->v_extra; \
		(out_fp) = (tv)->v.lightfunc; \
	} while (0)
#define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv) ((tv)->v.lightfunc)
#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv)   ((duk_small_uint_t) ((tv)->v_extra))
#define DUK_TVAL_GET_STRING(tv)            ((tv)->v.hstring)
#define DUK_TVAL_GET_OBJECT(tv)            ((tv)->v.hobject)
#define DUK_TVAL_GET_BUFFER(tv)            ((tv)->v.hbuffer)
#define DUK_TVAL_GET_HEAPHDR(tv)           ((tv)->v.heaphdr)

/* decoding */
#define DUK_TVAL_GET_TAG(tv)               ((tv)->t)
#define DUK_TVAL_IS_UNDEFINED(tv)          ((tv)->t == DUK_TAG_UNDEFINED)
#define DUK_TVAL_IS_UNUSED(tv)             ((tv)->t == DUK_TAG_UNUSED)
#define DUK_TVAL_IS_NULL(tv)               ((tv)->t == DUK_TAG_NULL)
#define DUK_TVAL_IS_BOOLEAN(tv)            ((tv)->t == DUK_TAG_BOOLEAN)
#define DUK_TVAL_IS_BOOLEAN_TRUE(tv)       (((tv)->t == DUK_TAG_BOOLEAN) && ((tv)->v.i != 0))
#define DUK_TVAL_IS_BOOLEAN_FALSE(tv)      (((tv)->t == DUK_TAG_BOOLEAN) && ((tv)->v.i == 0))
#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_IS_DOUBLE(tv)             ((tv)->t == DUK_TAG_NUMBER)
#define DUK_TVAL_IS_FASTINT(tv)            ((tv)->t == DUK_TAG_FASTINT)
#define DUK_TVAL_IS_NUMBER(tv)             ((tv)->t == DUK_TAG_NUMBER || \
                                            (tv)->t == DUK_TAG_FASTINT)
#else
#define DUK_TVAL_IS_NUMBER(tv)             ((tv)->t == DUK_TAG_NUMBER)
#define DUK_TVAL_IS_DOUBLE(tv)             DUK_TVAL_IS_NUMBER((tv))
#endif  /* DUK_USE_FASTINT */
#define DUK_TVAL_IS_POINTER(tv)            ((tv)->t == DUK_TAG_POINTER)

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

#define DUK_STRIDX_TO_LOCALE_STRING                                   39                             /* 'toLocaleString' */
#define DUK_HEAP_STRING_TO_LOCALE_STRING(heap)                        DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_LOCALE_STRING)
#define DUK_HTHREAD_STRING_TO_LOCALE_STRING(thr)                      DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_LOCALE_STRING)
#define DUK_STRIDX_VALUE_OF                                           40                             /* 'valueOf' */
#define DUK_HEAP_STRING_VALUE_OF(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VALUE_OF)
#define DUK_HTHREAD_STRING_VALUE_OF(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VALUE_OF)
#define DUK_STRIDX_TO_UTC_STRING                                      41                             /* 'toUTCString' */
#define DUK_HEAP_STRING_TO_UTC_STRING(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_UTC_STRING)
#define DUK_HTHREAD_STRING_TO_UTC_STRING(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_UTC_STRING)
#define DUK_STRIDX_TO_ISO_STRING                                      42                             /* 'toISOString' */
#define DUK_HEAP_STRING_TO_ISO_STRING(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_ISO_STRING)
#define DUK_HTHREAD_STRING_TO_ISO_STRING(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_ISO_STRING)
#define DUK_STRIDX_TO_GMT_STRING                                      43                             /* 'toGMTString' */
#define DUK_HEAP_STRING_TO_GMT_STRING(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_GMT_STRING)
#define DUK_HTHREAD_STRING_TO_GMT_STRING(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_GMT_STRING)
#define DUK_STRIDX_SOURCE                                             44                             /* 'source' */
#define DUK_HEAP_STRING_SOURCE(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SOURCE)
#define DUK_HTHREAD_STRING_SOURCE(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SOURCE)
#define DUK_STRIDX_IGNORE_CASE                                        45                             /* 'ignoreCase' */
#define DUK_HEAP_STRING_IGNORE_CASE(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IGNORE_CASE)
#define DUK_HTHREAD_STRING_IGNORE_CASE(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IGNORE_CASE)
#define DUK_STRIDX_MULTILINE                                          46                             /* 'multiline' */
#define DUK_HEAP_STRING_MULTILINE(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MULTILINE)
#define DUK_HTHREAD_STRING_MULTILINE(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MULTILINE)
#define DUK_STRIDX_LAST_INDEX                                         47                             /* 'lastIndex' */
#define DUK_HEAP_STRING_LAST_INDEX(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LAST_INDEX)
#define DUK_HTHREAD_STRING_LAST_INDEX(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LAST_INDEX)
#define DUK_STRIDX_FLAGS                                              48                             /* 'flags' */
#define DUK_HEAP_STRING_FLAGS(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLAGS)
#define DUK_HTHREAD_STRING_FLAGS(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLAGS)
#define DUK_STRIDX_INDEX                                              49                             /* 'index' */
#define DUK_HEAP_STRING_INDEX(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INDEX)
#define DUK_HTHREAD_STRING_INDEX(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INDEX)
#define DUK_STRIDX_PROTOTYPE                                          50                             /* 'prototype' */
#define DUK_HEAP_STRING_PROTOTYPE(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PROTOTYPE)
#define DUK_HTHREAD_STRING_PROTOTYPE(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PROTOTYPE)
#define DUK_STRIDX_CONSTRUCTOR                                        51                             /* 'constructor' */
#define DUK_HEAP_STRING_CONSTRUCTOR(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONSTRUCTOR)
#define DUK_HTHREAD_STRING_CONSTRUCTOR(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONSTRUCTOR)
#define DUK_STRIDX_MESSAGE                                            52                             /* 'message' */
#define DUK_HEAP_STRING_MESSAGE(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MESSAGE)
#define DUK_HTHREAD_STRING_MESSAGE(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MESSAGE)
#define DUK_STRIDX_LC_BOOLEAN                                         53                             /* 'boolean' */
#define DUK_HEAP_STRING_LC_BOOLEAN(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_BOOLEAN)
#define DUK_HTHREAD_STRING_LC_BOOLEAN(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_BOOLEAN)
#define DUK_STRIDX_LC_NUMBER                                          54                             /* 'number' */
#define DUK_HEAP_STRING_LC_NUMBER(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_NUMBER)
#define DUK_HTHREAD_STRING_LC_NUMBER(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_NUMBER)
#define DUK_STRIDX_LC_STRING                                          55                             /* 'string' */
#define DUK_HEAP_STRING_LC_STRING(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_STRING)
#define DUK_HTHREAD_STRING_LC_STRING(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_STRING)
#define DUK_STRIDX_LC_SYMBOL                                          56                             /* 'symbol' */
#define DUK_HEAP_STRING_LC_SYMBOL(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_SYMBOL)
#define DUK_HTHREAD_STRING_LC_SYMBOL(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_SYMBOL)
#define DUK_STRIDX_LC_OBJECT                                          57                             /* 'object' */
#define DUK_HEAP_STRING_LC_OBJECT(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_OBJECT)
#define DUK_HTHREAD_STRING_LC_OBJECT(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_OBJECT)
#define DUK_STRIDX_LC_UNDEFINED                                       58                             /* 'undefined' */
#define DUK_HEAP_STRING_LC_UNDEFINED(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_UNDEFINED)
#define DUK_HTHREAD_STRING_LC_UNDEFINED(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_UNDEFINED)
#define DUK_STRIDX_NAN                                                59                             /* 'NaN' */
#define DUK_HEAP_STRING_NAN(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NAN)
#define DUK_HTHREAD_STRING_NAN(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NAN)
#define DUK_STRIDX_INFINITY                                           60                             /* 'Infinity' */
#define DUK_HEAP_STRING_INFINITY(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INFINITY)
#define DUK_HTHREAD_STRING_INFINITY(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INFINITY)
#define DUK_STRIDX_MINUS_INFINITY                                     61                             /* '-Infinity' */
#define DUK_HEAP_STRING_MINUS_INFINITY(heap)                          DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MINUS_INFINITY)
#define DUK_HTHREAD_STRING_MINUS_INFINITY(thr)                        DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MINUS_INFINITY)
#define DUK_STRIDX_MINUS_ZERO                                         62                             /* '-0' */
#define DUK_HEAP_STRING_MINUS_ZERO(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MINUS_ZERO)
#define DUK_HTHREAD_STRING_MINUS_ZERO(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MINUS_ZERO)
#define DUK_STRIDX_COMMA                                              63                             /* ',' */
#define DUK_HEAP_STRING_COMMA(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_COMMA)
#define DUK_HTHREAD_STRING_COMMA(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_COMMA)
#define DUK_STRIDX_NEWLINE_4SPACE                                     64                             /* '\n    ' */
#define DUK_HEAP_STRING_NEWLINE_4SPACE(heap)                          DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NEWLINE_4SPACE)
#define DUK_HTHREAD_STRING_NEWLINE_4SPACE(thr)                        DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NEWLINE_4SPACE)
#define DUK_STRIDX_BRACKETED_ELLIPSIS                                 65                             /* '[...]' */
#define DUK_HEAP_STRING_BRACKETED_ELLIPSIS(heap)                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BRACKETED_ELLIPSIS)
#define DUK_HTHREAD_STRING_BRACKETED_ELLIPSIS(thr)                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BRACKETED_ELLIPSIS)
#define DUK_STRIDX_INVALID_DATE                                       66                             /* 'Invalid Date' */
#define DUK_HEAP_STRING_INVALID_DATE(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INVALID_DATE)
#define DUK_HTHREAD_STRING_INVALID_DATE(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INVALID_DATE)
#define DUK_STRIDX_LC_ARGUMENTS                                       67                             /* 'arguments' */
#define DUK_HEAP_STRING_LC_ARGUMENTS(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_ARGUMENTS)
#define DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_ARGUMENTS)
#define DUK_STRIDX_CALLEE                                             68                             /* 'callee' */
#define DUK_HEAP_STRING_CALLEE(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CALLEE)
#define DUK_HTHREAD_STRING_CALLEE(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CALLEE)
#define DUK_STRIDX_CALLER                                             69                             /* 'caller' */
#define DUK_HEAP_STRING_CALLER(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CALLER)
#define DUK_HTHREAD_STRING_CALLER(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CALLER)
#define DUK_STRIDX_APPLY                                              70                             /* 'apply' */
#define DUK_HEAP_STRING_APPLY(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_APPLY)
#define DUK_HTHREAD_STRING_APPLY(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_APPLY)
#define DUK_STRIDX_CONSTRUCT                                          71                             /* 'construct' */
#define DUK_HEAP_STRING_CONSTRUCT(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONSTRUCT)
#define DUK_HTHREAD_STRING_CONSTRUCT(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONSTRUCT)
#define DUK_STRIDX_DELETE_PROPERTY                                    72                             /* 'deleteProperty' */
#define DUK_HEAP_STRING_DELETE_PROPERTY(heap)                         DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DELETE_PROPERTY)
#define DUK_HTHREAD_STRING_DELETE_PROPERTY(thr)                       DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DELETE_PROPERTY)
#define DUK_STRIDX_GET                                                73                             /* 'get' */
#define DUK_HEAP_STRING_GET(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_GET)
#define DUK_HTHREAD_STRING_GET(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_GET)
#define DUK_STRIDX_HAS                                                74                             /* 'has' */
#define DUK_HEAP_STRING_HAS(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HAS)
#define DUK_HTHREAD_STRING_HAS(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HAS)
#define DUK_STRIDX_OWN_KEYS                                           75                             /* 'ownKeys' */
#define DUK_HEAP_STRING_OWN_KEYS(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_OWN_KEYS)
#define DUK_HTHREAD_STRING_OWN_KEYS(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_OWN_KEYS)
#define DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE                      76                             /* '\x81Symbol.toPrimitive\xff' */
#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_TO_PRIMITIVE(heap)           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE)
#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_TO_PRIMITIVE(thr)         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE)
#define DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE                      77                             /* '\x81Symbol.hasInstance\xff' */
#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_HAS_INSTANCE(heap)           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE)
#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_HAS_INSTANCE(thr)         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE)
#define DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG                     78                             /* '\x81Symbol.toStringTag\xff' */
#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_TO_STRING_TAG(heap)          DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG)
#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_TO_STRING_TAG(thr)        DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG)
#define DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE              79                             /* '\x81Symbol.isConcatSpreadable\xff' */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

 * buffer.  This status may change independently of the duk_hbufobj if
 * the underlying buffer is dynamic and changes without the hbufobj
 * being changed.
 */
#define DUK_HBUFOBJ_FULL_SLICE(h) \
	(DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \
	((h)->offset == 0 && (h)->length == DUK_HBUFFER_GET_SIZE((h)->buf)))

/* Validate that the whole slice [0,length[ is contained in the underlying
 * buffer.  Caller must ensure 'buf' != NULL.
 */
#define DUK_HBUFOBJ_VALID_SLICE(h) \
	(DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \
	((h)->offset + (h)->length <= DUK_HBUFFER_GET_SIZE((h)->buf)))

/* Validate byte read/write for virtual 'offset', i.e. check that the
 * offset, taking into account h->offset, is within the underlying
 * buffer size.  This is a safety check which is needed to ensure
 * that even a misconfigured duk_hbufobj never causes memory unsafe
 * behavior (e.g. if an underlying dynamic buffer changes after being
 * setup).  Caller must ensure 'buf' != NULL.
 */
#define DUK_HBUFOBJ_VALID_BYTEOFFSET_INCL(h,off) \
	(DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \
	((h)->offset + (off) < DUK_HBUFFER_GET_SIZE((h)->buf)))

#define DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h,off) \
	(DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \
	((h)->offset + (off) <= DUK_HBUFFER_GET_SIZE((h)->buf)))

/* Clamp an input byte length (already assumed to be within the nominal
 * duk_hbufobj 'length') to the current dynamic buffer limits to yield
 * a byte length limit that's safe for memory accesses.  This value can
 * be invalidated by any side effect because it may trigger a user
 * callback that resizes the underlying buffer.
 */
#define DUK_HBUFOBJ_CLAMP_BYTELENGTH(h,len) \
	(DUK_ASSERT_EXPR((h) != NULL), \
	duk_hbufobj_clamp_bytelength((h), (len)))

/* Typed arrays have virtual indices, ArrayBuffer and DataView do not. */
#define DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h)  ((h)->is_typedarray)

struct duk_hbufobj {
	/* Shared object part. */
	duk_hobject obj;

	/* Underlying buffer (refcounted), may be NULL. */
	duk_hbuffer *buf;

	/* .buffer reference to an ArrayBuffer, may be NULL. */
	duk_hobject *buf_prop;

	/* Slice and accessor information.
	 *
	 * Because the underlying buffer may be dynamic, these may be
	 * invalidated by the buffer being modified so that both offset
	 * and length should be validated before every access.  Behavior
	 * when the underlying buffer has changed doesn't need to be clean:
	 * virtual 'length' doesn't need to be affected, reads can return
	 * zero/NaN, and writes can be ignored.
	 *
	 * Note that a data pointer cannot be precomputed because 'buf' may
	 * be dynamic and its pointer unstable.
	 */

	duk_uint_t offset;       /* byte offset to buf */
	duk_uint_t length;       /* byte index limit for element access, exclusive */
	duk_uint8_t shift;       /* element size shift:
	                          *   0 = u8/i8
	                          *   1 = u16/i16
	                          *   2 = u32/i32/float
	                          *   3 = double
	                          */
	duk_uint8_t elem_type;   /* element type */
	duk_uint8_t is_typedarray;
};

DUK_INTERNAL_DECL duk_uint_t duk_hbufobj_clamp_bytelength(duk_hbufobj *h_bufobj, duk_uint_t len);
DUK_INTERNAL_DECL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_hbuffer *h_buf);
DUK_INTERNAL_DECL void duk_hbufobj_push_validated_read(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size);
DUK_INTERNAL_DECL void duk_hbufobj_validated_write(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size);
DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_hthread *thr, duk_idx_t idx);

#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
#endif  /* DUK_HBUFOBJ_H_INCLUDED */
/* #include duk_hthread.h */
#line 1 "duk_hthread.h"
/*
 *  Heap thread object representation.
 *
 *  duk_hthread is also the 'context' for public API functions via a
 *  different typedef.  Most API calls operate on the topmost frame
 *  of the value stack only.
 */

#if !defined(DUK_HTHREAD_H_INCLUDED)
#define DUK_HTHREAD_H_INCLUDED

/*
 *  Stack constants
 */

/* Initial valstack size, roughly 0.7kiB. */
#define DUK_VALSTACK_INITIAL_SIZE       96U

/* Internal extra elements assumed on function entry, always added to
 * user-defined 'extra' for e.g. the duk_check_stack() call.
 */
#define DUK_VALSTACK_INTERNAL_EXTRA     32U

/* Number of elements guaranteed to be user accessible (in addition to call
 * arguments) on Duktape/C function entry.  This is the major public API
 * commitment.
 */
#define DUK_VALSTACK_API_ENTRY_MINIMUM  DUK_API_ENTRY_STACK

/*
 *  Activation defines
 */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

		DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \
	} while (0)

/* Check that there's room to push one value. */
#if defined(DUK_USE_VALSTACK_UNSAFE)
/* Faster but value stack overruns are memory unsafe. */
#define DUK__CHECK_SPACE() DUK__ASSERT_SPACE()
#else
#define DUK__CHECK_SPACE() do { \
		if (DUK_UNLIKELY(thr->valstack_top >= thr->valstack_end)) { \
			DUK_ERROR_RANGE_PUSH_BEYOND(thr); \
		} \
	} while (0)
#endif

DUK_LOCAL duk_small_uint_t duk__get_symbol_type(duk_hstring *h) {
	const duk_uint8_t *data;
	duk_size_t len;

	DUK_ASSERT(h != NULL);
	DUK_ASSERT(DUK_HSTRING_HAS_SYMBOL(h));
	DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h) >= 1);  /* always true, symbol prefix */

	data = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
	len = DUK_HSTRING_GET_BYTELEN(h);
	DUK_ASSERT(len >= 1);

	/* XXX: differentiate between 0x82 and 0xff (hidden vs. internal?)? */

	if (data[0] == 0xffU) {
		return DUK_SYMBOL_TYPE_HIDDEN;
	} else if (data[0] == 0x82U) {
		return DUK_SYMBOL_TYPE_HIDDEN;
	} else if (data[0] == 0x80U) {
		return DUK_SYMBOL_TYPE_GLOBAL;
	} else if (data[len - 1] != 0xffU) {
		return DUK_SYMBOL_TYPE_LOCAL;
	} else {
		return DUK_SYMBOL_TYPE_WELLKNOWN;
	}
}

DUK_LOCAL const char *duk__get_symbol_type_string(duk_hstring *h) {
	duk_small_uint_t idx;
	idx = duk__get_symbol_type(h);
	DUK_ASSERT(idx < sizeof(duk__symbol_type_strings));
	return duk__symbol_type_strings[idx];
}

DUK_LOCAL_DECL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t tag);

DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value, duk_bool_t require) {
	duk_tval *tv;
	duk_small_int_t c;
	duk_double_t d;

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);

	/*
	 *  Special cases like NaN and +/- Infinity are handled explicitly
	 *  because a plain C coercion from double to int handles these cases
	 *  in undesirable ways.  For instance, NaN may coerce to INT_MIN
	 *  (not zero), and INT_MAX + 1 may coerce to INT_MIN (not INT_MAX).
	 *
	 *  This double-to-int coercion differs from ToInteger() because it
	 *  has a finite range (ToInteger() allows e.g. +/- Infinity).  It
	 *  also differs from ToInt32() because the INT_MIN/INT_MAX clamping
	 *  depends on the size of the int type on the platform.  In particular,
	 *  on platforms with a 64-bit int type, the full range is allowed.
	 */

#if defined(DUK_USE_FASTINT)
	if (DUK_TVAL_IS_FASTINT(tv)) {
		duk_int64_t t = DUK_TVAL_GET_FASTINT(tv);
#if (DUK_INT_MAX <= 0x7fffffffL)
		/* Clamping only necessary for 32-bit ints. */
		if (t < DUK_INT_MIN) {
			t = DUK_INT_MIN;
		} else if (t > DUK_INT_MAX) {
			t = DUK_INT_MAX;
		}
#endif
		return (duk_int_t) t;
	}
#endif

	if (DUK_TVAL_IS_NUMBER(tv)) {
		d = DUK_TVAL_GET_NUMBER(tv);
		c = (duk_small_int_t) DUK_FPCLASSIFY(d);
		if (c == DUK_FP_NAN) {
			return 0;
		} else if (d < (duk_double_t) DUK_INT_MIN) {
			/* covers -Infinity */
			return DUK_INT_MIN;
		} else if (d > (duk_double_t) DUK_INT_MAX) {
			/* covers +Infinity */
			return DUK_INT_MAX;
		} else {
			/* coerce towards zero */
			return (duk_int_t) d;
		}
	}

	if (require) {
		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER);
		DUK_WO_NORETURN(return 0;);
	}

	return def_value;
}

DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value, duk_bool_t require) {
	duk_tval *tv;
	duk_small_int_t c;
	duk_double_t d;

	/* Same as above but for unsigned int range. */

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);

#if defined(DUK_USE_FASTINT)

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

		/* Not guaranteed to be 0 or 1. */
	}

	return ret;
}

DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);

	return duk__get_boolean_raw(thr, idx, 0);  /* default: false */
}

DUK_EXTERNAL duk_bool_t duk_get_boolean_default(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) {
	DUK_ASSERT_API_ENTRY(thr);

	return duk__get_boolean_raw(thr, idx, def_value);
}

DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_hthread *thr, duk_idx_t idx) {
	duk_tval *tv;
	duk_bool_t ret;

	DUK_ASSERT_API_ENTRY(thr);

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);
	if (DUK_LIKELY(DUK_TVAL_IS_BOOLEAN(tv))) {
		ret = DUK_TVAL_GET_BOOLEAN(tv);
		DUK_ASSERT(ret == 0 || ret == 1);
		return ret;
	} else {
		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "boolean", DUK_STR_NOT_BOOLEAN);
		DUK_WO_NORETURN(return 0;);
	}
}

DUK_EXTERNAL duk_bool_t duk_opt_boolean(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) {
	DUK_ASSERT_API_ENTRY(thr);

	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
		return def_value;
	}
	return duk_require_boolean(thr, idx);
}

DUK_LOCAL DUK_ALWAYS_INLINE duk_double_t duk__get_number_raw(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) {
	duk_double_union ret;
	duk_tval *tv;

	DUK_ASSERT_CTX_VALID(thr);

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);
#if defined(DUK_USE_FASTINT)
	if (DUK_TVAL_IS_FASTINT(tv)) {
		ret.d = (duk_double_t) DUK_TVAL_GET_FASTINT(tv);  /* XXX: cast trick */
	}
	else
#endif
	if (DUK_TVAL_IS_DOUBLE(tv)) {
		/* When using packed duk_tval, number must be in NaN-normalized form
		 * for it to be a duk_tval, so no need to normalize.  NOP for unpacked
		 * duk_tval.
		 */
		ret.d = DUK_TVAL_GET_DOUBLE(tv);
		DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&ret));
	} else {
		ret.d = def_value;
		/* Default value (including NaN) may not be normalized. */
	}

	return ret.d;
}

DUK_EXTERNAL duk_double_t duk_get_number(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk__get_number_raw(thr, idx, DUK_DOUBLE_NAN);  /* default: NaN */
}

DUK_EXTERNAL duk_double_t duk_get_number_default(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk__get_number_raw(thr, idx, def_value);
}

DUK_EXTERNAL duk_double_t duk_require_number(duk_hthread *thr, duk_idx_t idx) {
	duk_tval *tv;
	duk_double_union ret;

	DUK_ASSERT_API_ENTRY(thr);

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);
	if (DUK_UNLIKELY(!DUK_TVAL_IS_NUMBER(tv))) {
		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER);
		DUK_WO_NORETURN(return 0.0;);
	}

	ret.d = DUK_TVAL_GET_NUMBER(tv);

	/* When using packed duk_tval, number must be in NaN-normalized form
	 * for it to be a duk_tval, so no need to normalize.  NOP for unpacked
	 * duk_tval.
	 */
	DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&ret));
	return ret.d;
}

DUK_EXTERNAL duk_double_t duk_opt_number(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) {
	DUK_ASSERT_API_ENTRY(thr);

	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
		/* User provided default is not NaN normalized. */
		return def_value;
	}
	return duk_require_number(thr, idx);
}

DUK_EXTERNAL duk_int_t duk_get_int(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);

	return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 0 /*require*/);
}

DUK_EXTERNAL duk_uint_t duk_get_uint(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);

	return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 0 /*require*/);
}

DUK_EXTERNAL duk_int_t duk_get_int_default(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value) {
	DUK_ASSERT_API_ENTRY(thr);

	return (duk_int_t) duk__api_coerce_d2i(thr, idx, def_value, 0 /*require*/);
}

DUK_EXTERNAL duk_uint_t duk_get_uint_default(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value) {
	DUK_ASSERT_API_ENTRY(thr);

	return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, def_value, 0 /*require*/);
}

DUK_EXTERNAL duk_int_t duk_require_int(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);

	return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 1 /*require*/);
}

DUK_EXTERNAL duk_uint_t duk_require_uint(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);

	return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 1 /*require*/);
}

DUK_EXTERNAL duk_int_t duk_opt_int(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value) {
	DUK_ASSERT_API_ENTRY(thr);

	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
		return def_value;
	}
	return duk_require_int(thr, idx);
}

DUK_EXTERNAL duk_uint_t duk_opt_uint(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value) {
	DUK_ASSERT_API_ENTRY(thr);

	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
		return def_value;
	}
	return duk_require_uint(thr, idx);
}

DUK_EXTERNAL const char *duk_get_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	ret = duk_js_toint32(thr, tv);

	/* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
	tv = duk_require_tval(thr, idx);
	DUK_TVAL_SET_I32_UPDREF(thr, tv, ret);  /* side effects */
	return ret;
}

DUK_EXTERNAL duk_uint32_t duk_to_uint32(duk_hthread *thr, duk_idx_t idx) {
	duk_tval *tv;
	duk_uint32_t ret;

	DUK_ASSERT_API_ENTRY(thr);

	tv = duk_require_tval(thr, idx);
	DUK_ASSERT(tv != NULL);
	ret = duk_js_touint32(thr, tv);

	/* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
	tv = duk_require_tval(thr, idx);
	DUK_TVAL_SET_U32_UPDREF(thr, tv, ret);  /* side effects */
	return ret;
}

DUK_EXTERNAL duk_uint16_t duk_to_uint16(duk_hthread *thr, duk_idx_t idx) {
	duk_tval *tv;
	duk_uint16_t ret;

	DUK_ASSERT_API_ENTRY(thr);

	tv = duk_require_tval(thr, idx);
	DUK_ASSERT(tv != NULL);
	ret = duk_js_touint16(thr, tv);

	/* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
	tv = duk_require_tval(thr, idx);
	DUK_TVAL_SET_U32_UPDREF(thr, tv, ret);  /* side effects */
	return ret;
}

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
/* Special coercion for Uint8ClampedArray. */
DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_hthread *thr, duk_idx_t idx) {
	duk_double_t d;
	duk_double_t t;
	duk_uint8_t ret;

	DUK_ASSERT_API_ENTRY(thr);

	/* XXX: Simplify this algorithm, should be possible to come up with
	 * a shorter and faster algorithm by inspecting IEEE representation
	 * directly.
	 */

	d = duk_to_number(thr, idx);
	if (d <= 0.0) {
		return 0;
	} else if (d >= 255) {
		return 255;
	} else if (DUK_ISNAN(d)) {
		/* Avoid NaN-to-integer coercion as it is compiler specific. */
		return 0;
	}

	t = d - DUK_FLOOR(d);
	if (t == 0.5) {
		/* Exact halfway, round to even. */
		ret = (duk_uint8_t) d;
		ret = (ret + 1) & 0xfe;  /* Example: d=3.5, t=0.5 -> ret = (3 + 1) & 0xfe = 4 & 0xfe = 4
		                          * Example: d=4.5, t=0.5 -> ret = (4 + 1) & 0xfe = 5 & 0xfe = 4
		                          */
	} else {
		/* Not halfway, round to nearest. */
		ret = (duk_uint8_t) (d + 0.5);
	}
	return ret;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

DUK_EXTERNAL const char *duk_to_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
	DUK_ASSERT_API_ENTRY(thr);

	(void) duk_to_string(thr, idx);
	DUK_ASSERT(duk_is_string(thr, idx));
	return duk_require_lstring(thr, idx, out_len);
}

DUK_LOCAL duk_ret_t duk__safe_to_string_raw(duk_hthread *thr, void *udata) {
	DUK_ASSERT_CTX_VALID(thr);
	DUK_UNREF(udata);

	duk_to_string(thr, -1);
	return 1;
}

DUK_EXTERNAL const char *duk_safe_to_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
	DUK_ASSERT_API_ENTRY(thr);

	idx = duk_require_normalize_index(thr, idx);

	/* We intentionally ignore the duk_safe_call() return value and only
	 * check the output type.  This way we don't also need to check that
	 * the returned value is indeed a string in the success case.
	 */

	duk_dup(thr, idx);
	(void) duk_safe_call(thr, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
	if (!duk_is_string(thr, -1)) {
		/* Error: try coercing error to string once. */
		(void) duk_safe_call(thr, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
		if (!duk_is_string(thr, -1)) {
			/* Double error */
			duk_pop_unsafe(thr);
			duk_push_hstring_stridx(thr, DUK_STRIDX_UC_ERROR);
		} else {
			;
		}
	} else {
		/* String; may be a symbol, accepted. */
		;
	}

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

}

DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_hthread *thr, duk_idx_t idx) {
	duk_tval *tv;

	DUK_ASSERT_API_ENTRY(thr);

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);

	return duk_get_type_mask_tval(tv);
}

DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t mask) {
	DUK_ASSERT_API_ENTRY(thr);

	if (DUK_LIKELY((duk_get_type_mask(thr, idx) & mask) != 0U)) {
		return 1;
	}
	if (mask & DUK_TYPE_MASK_THROW) {
		DUK_ERROR_TYPE(thr, DUK_STR_UNEXPECTED_TYPE);
		DUK_WO_NORETURN(return 0;);
	}
	return 0;
}

DUK_EXTERNAL duk_bool_t duk_is_undefined(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk__tag_check(thr, idx, DUK_TAG_UNDEFINED);
}

DUK_EXTERNAL duk_bool_t duk_is_null(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk__tag_check(thr, idx, DUK_TAG_NULL);
}

DUK_EXTERNAL duk_bool_t duk_is_boolean(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk__tag_check(thr, idx, DUK_TAG_BOOLEAN);
}

DUK_EXTERNAL duk_bool_t duk_is_number(duk_hthread *thr, duk_idx_t idx) {
	duk_tval *tv;

	DUK_ASSERT_API_ENTRY(thr);

	/*
	 *  Number is special because it doesn't have a specific
	 *  tag in the 8-byte representation.
	 */

	/* XXX: shorter version for unpacked representation? */

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);
	return DUK_TVAL_IS_NUMBER(tv);
}

DUK_EXTERNAL duk_bool_t duk_is_nan(duk_hthread *thr, duk_idx_t idx) {
	/* XXX: This will now return false for non-numbers, even though they would
	 * coerce to NaN (as a general rule).  In particular, duk_get_number()
	 * returns a NaN for non-numbers, so should this function also return
	 * true for non-numbers?
	 */

	duk_tval *tv;

	DUK_ASSERT_API_ENTRY(thr);

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);

	/* XXX: for packed duk_tval an explicit "is number" check is unnecessary */
	if (!DUK_TVAL_IS_NUMBER(tv)) {
		return 0;
	}
	return (duk_bool_t) DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv));
}

DUK_EXTERNAL duk_bool_t duk_is_string(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk__tag_check(thr, idx, DUK_TAG_STRING);
}

DUK_INTERNAL duk_bool_t duk_is_string_notsymbol(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk_get_hstring_notsymbol(thr, idx) != NULL;
}

DUK_EXTERNAL duk_bool_t duk_is_object(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk__tag_check(thr, idx, DUK_TAG_OBJECT);
}

DUK_EXTERNAL duk_bool_t duk_is_buffer(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);
	return duk__tag_check(thr, idx, DUK_TAG_BUFFER);
}

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) {
	duk_tval *tv;

	DUK_ASSERT_API_ENTRY(thr);

	tv = duk_get_tval_or_unused(thr, idx);
	DUK_ASSERT(tv != NULL);
	if (DUK_TVAL_IS_BUFFER(tv)) {
		return 1;
	} else if (DUK_TVAL_IS_OBJECT(tv)) {
		duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
		DUK_ASSERT(h != NULL);
		if (DUK_HOBJECT_IS_BUFOBJ(h)) {
			return 1;
		}
	}
	return 0;
}
#else  /* DUK_USE_BUFFEROBJECT_SUPPORT */
DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) {
	DUK_ASSERT_API_ENTRY(thr);

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN


	DUK_ASSERT_API_ENTRY(thr);
	DUK_ASSERT(tv != NULL);

	DUK__CHECK_SPACE();
	tv_slot = thr->valstack_top++;
	DUK_TVAL_SET_TVAL(tv_slot, tv);
	DUK_TVAL_INCREF(thr, tv);  /* no side effects */
}

DUK_EXTERNAL void duk_push_undefined(duk_hthread *thr) {
	DUK_ASSERT_API_ENTRY(thr);

	DUK__CHECK_SPACE();

	/* Because value stack init policy is 'undefined above top',
	 * we don't need to write, just assert.
	 */
	thr->valstack_top++;
	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1));
}

DUK_EXTERNAL void duk_push_null(duk_hthread *thr) {
	duk_tval *tv_slot;

	DUK_ASSERT_API_ENTRY(thr);
	DUK__CHECK_SPACE();
	tv_slot = thr->valstack_top++;
	DUK_TVAL_SET_NULL(tv_slot);
}

DUK_EXTERNAL void duk_push_boolean(duk_hthread *thr, duk_bool_t val) {
	duk_tval *tv_slot;
	duk_small_int_t b;

	DUK_ASSERT_API_ENTRY(thr);
	DUK__CHECK_SPACE();
	b = (val ? 1 : 0);  /* ensure value is 1 or 0 (not other non-zero) */
	tv_slot = thr->valstack_top++;
	DUK_TVAL_SET_BOOLEAN(tv_slot, b);
}

DUK_EXTERNAL void duk_push_true(duk_hthread *thr) {
	duk_tval *tv_slot;

	DUK_ASSERT_API_ENTRY(thr);
	DUK__CHECK_SPACE();
	tv_slot = thr->valstack_top++;
	DUK_TVAL_SET_BOOLEAN_TRUE(tv_slot);
}

DUK_EXTERNAL void duk_push_false(duk_hthread *thr) {
	duk_tval *tv_slot;

	DUK_ASSERT_API_ENTRY(thr);
	DUK__CHECK_SPACE();
	tv_slot = thr->valstack_top++;
	DUK_TVAL_SET_BOOLEAN_FALSE(tv_slot);
}

/* normalize NaN which may not match our canonical internal NaN */
DUK_EXTERNAL void duk_push_number(duk_hthread *thr, duk_double_t val) {
	duk_tval *tv_slot;
	duk_double_union du;

	DUK_ASSERT_API_ENTRY(thr);
	DUK__CHECK_SPACE();
	du.d = val;
	DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
	tv_slot = thr->valstack_top++;
	DUK_TVAL_SET_NUMBER(tv_slot, du.d);
}

DUK_EXTERNAL void duk_push_int(duk_hthread *thr, duk_int_t val) {
#if defined(DUK_USE_FASTINT)
	duk_tval *tv_slot;

	DUK_ASSERT_API_ENTRY(thr);
	DUK__CHECK_SPACE();
	tv_slot = thr->valstack_top++;
#if DUK_INT_MAX <= 0x7fffffffL
	DUK_TVAL_SET_I32(tv_slot, (duk_int32_t) val);
#else
	if (val >= DUK_FASTINT_MIN && val <= DUK_FASTINT_MAX) {
		DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val);
	} else {
		duk_double_t = (duk_double_t) val;
		DUK_TVAL_SET_NUMBER(tv_slot, d);
	}
#endif
#else  /* DUK_USE_FASTINT */
	duk_tval *tv_slot;
	duk_double_t d;

	DUK_ASSERT_API_ENTRY(thr);
	DUK__CHECK_SPACE();
	d = (duk_double_t) val;
	tv_slot = thr->valstack_top++;
	DUK_TVAL_SET_NUMBER(tv_slot, d);
#endif  /* DUK_USE_FASTINT */
}

DUK_EXTERNAL void duk_push_uint(duk_hthread *thr, duk_uint_t val) {
#if defined(DUK_USE_FASTINT)
	duk_tval *tv_slot;

	DUK_ASSERT_API_ENTRY(thr);
	DUK__CHECK_SPACE();
	tv_slot = thr->valstack_top++;
#if DUK_UINT_MAX <= 0xffffffffUL
	DUK_TVAL_SET_U32(tv_slot, (duk_uint32_t) val);
#else
	if (val <= DUK_FASTINT_MAX) {  /* val is unsigned so >= 0 */
		/* XXX: take advantage of val being unsigned, no need to mask */
		DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val);
	} else {
		duk_double_t = (duk_double_t) val;
		DUK_TVAL_SET_NUMBER(tv_slot, d);
	}
#endif
#else  /* DUK_USE_FASTINT */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN


	DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld, have1=%ld, have2=%ld, val1=%!T, val2=%!T",
	                     (long) idx1, (long) idx2, (long) have1, (long) have2,
	                     (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));

	if (have1) {
		if (have2) {
			;
		} else {
			ret = -1;
			goto pop_ret;
		}
	} else {
		if (have2) {
			ret = 1;
			goto pop_ret;
		} else {
			ret = 0;
			goto pop_ret;
		}
	}

	undef1 = duk_is_undefined(thr, -2);
	undef2 = duk_is_undefined(thr, -1);
	if (undef1) {
		if (undef2) {
			ret = 0;
			goto pop_ret;
		} else {
			ret = 1;
			goto pop_ret;
		}
	} else {
		if (undef2) {
			ret = -1;
			goto pop_ret;
		} else {
			;
		}
	}

	if (!duk_is_undefined(thr, idx_fn)) {
		duk_double_t d;

		/* No need to check callable; duk_call() will do that. */
		duk_dup(thr, idx_fn);    /* -> [ ... x y fn ] */
		duk_insert(thr, -3);     /* -> [ ... fn x y ] */
		duk_call(thr, 2);        /* -> [ ... res ] */

		/* ES5 is a bit vague about what to do if the return value is
		 * not a number.  ES2015 provides a concrete description:
		 * http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare.
		 */

		d = duk_to_number_m1(thr);
		if (d < 0.0) {
			ret = -1;
		} else if (d > 0.0) {
			ret = 1;
		} else {
			/* Because NaN compares to false, NaN is handled here
			 * without an explicit check above.
			 */
			ret = 0;
		}

		duk_pop_nodecref_unsafe(thr);
		DUK_DDD(DUK_DDDPRINT("-> result %ld (from comparefn, after coercion)", (long) ret));
		return ret;
	}

	/* string compare is the default (a bit oddly) */

	/* XXX: any special handling for plain array; causes repeated coercion now? */
	h1 = duk_to_hstring(thr, -2);
	h2 = duk_to_hstring_m1(thr);
	DUK_ASSERT(h1 != NULL);
	DUK_ASSERT(h2 != NULL);

	ret = duk_js_string_compare(h1, h2);  /* retval is directly usable */
	goto pop_ret;

 pop_ret:
	duk_pop_2_unsafe(thr);
	DUK_DDD(DUK_DDDPRINT("-> result %ld", (long) ret));
	return ret;
}

DUK_LOCAL void duk__array_sort_swap(duk_hthread *thr, duk_int_t l, duk_int_t r) {
	duk_bool_t have_l, have_r;
	duk_idx_t idx_obj = 1;  /* fixed offset in valstack */

	if (l == r) {
		return;
	}

	/* swap elements; deal with non-existent elements correctly */
	have_l = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) l);
	have_r = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) r);

	if (have_r) {
		/* right exists, [[Put]] regardless whether or not left exists */
		duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) l);
	} else {
		duk_del_prop_index(thr, idx_obj, (duk_uarridx_t) l);
		duk_pop_undefined(thr);
	}

	if (have_l) {
		duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) r);
	} else {
		duk_del_prop_index(thr, idx_obj, (duk_uarridx_t) r);
		duk_pop_undefined(thr);
	}
}

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
/* Debug print which visualizes the qsort partitioning process. */
DUK_LOCAL void duk__debuglog_qsort_state(duk_hthread *thr, duk_int_t lo, duk_int_t hi, duk_int_t pivot) {
	char buf[4096];
	char *ptr = buf;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

			i_step = 1;
			i_end = field_bytelen;  /* one i_step over */
		} else {
			/* Gather in little endian */
			i = field_bytelen - 1;
			i_step = -1;
			i_end = -1;  /* one i_step over */
		}

#if defined(DUK_USE_64BIT_OPS)
		tmp = 0;
		do {
			DUK_ASSERT(i >= 0 && i < field_bytelen);
			tmp = (tmp << 8) + (duk_int64_t) p[i];
			i += i_step;
		} while (i != i_end);

		if (magic_signed) {
			/* Shift to sign extend.  Left shift must be unsigned
			 * to avoid undefined behavior; right shift must be
			 * signed to sign extend properly.
			 */
			shift_tmp = (duk_small_uint_t) (64U - (duk_small_uint_t) field_bytelen * 8U);
			tmp = (duk_int64_t) ((duk_uint64_t) tmp << shift_tmp) >> shift_tmp;
		}

		duk_push_i64(thr, tmp);
#else
		highbyte = p[i];
		if (magic_signed && (highbyte & 0x80) != 0) {
			/* 0xff => 255 - 256 = -1; 0x80 => 128 - 256 = -128 */
			tmp = (duk_double_t) (highbyte - 256);
		} else {
			tmp = (duk_double_t) highbyte;
		}
		for (;;) {
			i += i_step;
			if (i == i_end) {
				break;
			}
			DUK_ASSERT(i >= 0 && i < field_bytelen);
			tmp = (tmp * 256.0) + (duk_double_t) p[i];
		}

		duk_push_number(thr, tmp);
#endif
		break;
	}
	default: {  /* should never happen but default here */
		goto fail_bounds;
	}
	}

	return 1;

 fail_neutered:
 fail_field_length:
 fail_bounds:
	if (no_assert) {
		/* Node.js return value for noAssert out-of-bounds reads is
		 * usually (but not always) NaN.  Return NaN consistently.
		 */
		duk_push_nan(thr);
		return 1;
	}
	DUK_DCERROR_RANGE_INVALID_ARGS(thr);
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
/* XXX: split into separate functions for each field type? */
DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_hthread *thr) {
	duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(thr);
	duk_small_int_t magic_ftype;
	duk_small_int_t magic_bigendian;
	duk_small_int_t magic_signed;
	duk_small_int_t magic_typedarray;
	duk_small_int_t endswap;
	duk_hbufobj *h_this;
	duk_bool_t no_assert;
	duk_int_t offset_signed;
	duk_uint_t offset;
	duk_uint_t buffer_length;
	duk_uint_t check_length;
	duk_uint8_t *buf;
	duk_double_union du;
	duk_int_t nbytes = 0;

	magic_ftype = magic & 0x0007;
	magic_bigendian = magic & 0x0008;
	magic_signed = magic & 0x0010;
	magic_typedarray = magic & 0x0020;
	DUK_UNREF(magic_signed);

	h_this = duk__require_bufobj_this(thr);  /* XXX: very inefficient for plain buffers */
	DUK_ASSERT(h_this != NULL);
	buffer_length = h_this->length;

	/* [ value  offset noAssert                 ], when ftype != DUK__FLD_VARINT */
	/* [ value  offset fieldByteLength noAssert ], when ftype == DUK__FLD_VARINT */
	/* [ offset value  littleEndian             ], when DUK__FLD_TYPEDARRAY (regardless of ftype) */

	/* Handle TypedArray vs. Node.js Buffer arg differences */
	if (magic_typedarray) {
		no_assert = 0;
#if defined(DUK_USE_INTEGER_LE)
		endswap = !duk_to_boolean(thr, 2);  /* 1=little endian */
#else
		endswap = duk_to_boolean(thr, 2);  /* 1=little endian */
#endif
		duk_swap(thr, 0, 1);  /* offset/value order different from Node.js */
	} else {
		no_assert = duk_to_boolean(thr, (magic_ftype == DUK__FLD_VARINT) ? 3 : 2);
#if defined(DUK_USE_INTEGER_LE)
		endswap = magic_bigendian;
#else
		endswap = !magic_bigendian;
#endif
	}

	/* Offset is coerced first to signed integer range and then to unsigned.

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

			goto fail_bounds;
		}
		du.f[0] = (duk_float_t) duk_to_number(thr, 0);
		if (endswap) {
			tmp = du.ui[0];
			tmp = DUK_BSWAP32(tmp);
			du.ui[0] = tmp;
		}
		/* sign doesn't matter when writing */
		duk_memcpy((void *) (buf + offset), (const void *) du.uc, 4);
		break;
	}
	case DUK__FLD_DOUBLE: {
		if (offset + 8U > check_length) {
			goto fail_bounds;
		}
		du.d = (duk_double_t) duk_to_number(thr, 0);
		if (endswap) {
			DUK_DBLUNION_BSWAP64(&du);
		}
		/* sign doesn't matter when writing */
		duk_memcpy((void *) (buf + offset), (const void *) du.uc, 8);
		break;
	}
	case DUK__FLD_VARINT: {
		/* Node.js Buffer variable width integer field.  We don't really
		 * care about speed here, so aim for shortest algorithm.
		 */
		duk_int_t field_bytelen;
		duk_int_t i, i_step, i_end;
#if defined(DUK_USE_64BIT_OPS)
		duk_int64_t tmp;
#else
		duk_double_t tmp;
#endif
		duk_uint8_t *p;

		field_bytelen = (duk_int_t) nbytes;
		if (offset + (duk_uint_t) field_bytelen > check_length) {
			goto fail_bounds;
		}

		/* Slow writing of value using either 64-bit arithmetic
		 * or IEEE doubles if 64-bit types not available.  There's
		 * no special sign handling when writing varints.
		 */

		if (magic_bigendian) {
			/* Write in big endian */
			i = field_bytelen;  /* one i_step added at top of loop */
			i_step = -1;
			i_end = 0;
		} else {
			/* Write in little endian */
			i = -1;  /* one i_step added at top of loop */
			i_step = 1;
			i_end = field_bytelen - 1;
		}

		/* XXX: The duk_to_number() cast followed by integer coercion
		 * is platform specific so NaN, +/- Infinity, and out-of-bounds
		 * values result in platform specific output now.
		 * See: test-bi-nodejs-buffer-proto-varint-special.js
		 */

#if defined(DUK_USE_64BIT_OPS)
		tmp = (duk_int64_t) duk_to_number(thr, 0);
		p = (duk_uint8_t *) (buf + offset);
		do {
			i += i_step;
			DUK_ASSERT(i >= 0 && i < field_bytelen);
			p[i] = (duk_uint8_t) (tmp & 0xff);
			tmp = tmp >> 8;  /* unnecessary shift for last byte */
		} while (i != i_end);
#else
		tmp = duk_to_number(thr, 0);
		p = (duk_uint8_t *) (buf + offset);
		do {
			i += i_step;
			tmp = DUK_FLOOR(tmp);
			DUK_ASSERT(i >= 0 && i < field_bytelen);
			p[i] = (duk_uint8_t) (DUK_FMOD(tmp, 256.0));
			tmp = tmp / 256.0;  /* unnecessary div for last byte */
		} while (i != i_end);
#endif
		break;
	}
	default: {  /* should never happen but default here */
		goto fail_bounds;
	}
	}

	/* Node.js Buffer: return offset + #bytes written (i.e. next
	 * write offset).
	 */
	if (magic_typedarray) {
		/* For TypedArrays 'undefined' return value is specified
		 * by ES2015 (matches V8).
		 */
		return 0;
	}
	duk_push_uint(thr, offset + (duk_uint_t) nbytes);
	return 1;

 fail_neutered:
 fail_field_length:
 fail_bounds:
	if (no_assert) {
		/* Node.js return value for failed writes is offset + #bytes
		 * that would have been written.
		 */
		/* XXX: for negative input offsets, 'offset' will be a large
		 * positive value so the result here is confusing.
		 */
		if (magic_typedarray) {
			return 0;
		}
		duk_push_uint(thr, offset + (duk_uint_t) nbytes);
		return 1;
	}
	DUK_DCERROR_RANGE_INVALID_ARGS(thr);

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

		 */
		return (a - b + 1) / b;
	}
}

/* Compute day number of the first day of a given year. */
DUK_LOCAL duk_int_t duk__day_from_year(duk_int_t year) {
	/* Note: in integer arithmetic, (x / 4) is same as floor(x / 4) for non-negative
	 * values, but is incorrect for negative ones.
	 */
	return 365 * (year - 1970)
	       + duk__div_floor(year - 1969, 4)
	       - duk__div_floor(year - 1901, 100)
	       + duk__div_floor(year - 1601, 400);
}

/* Given a day number, determine year and day-within-year. */
DUK_LOCAL duk_int_t duk__year_from_day(duk_int_t day, duk_small_int_t *out_day_within_year) {
	duk_int_t year;
	duk_int_t diff_days;

	/* estimate year upwards (towards positive infinity), then back down;
	 * two iterations should be enough
	 */

	if (day >= 0) {
		year = 1970 + day / 365;
	} else {
		year = 1970 + day / 366;
	}

	for (;;) {
		diff_days = duk__day_from_year(year) - day;
		DUK_DDD(DUK_DDDPRINT("year=%ld day=%ld, diff_days=%ld", (long) year, (long) day, (long) diff_days));
		if (diff_days <= 0) {
			DUK_ASSERT(-diff_days < 366);  /* fits into duk_small_int_t */
			*out_day_within_year = -diff_days;
			DUK_DDD(DUK_DDDPRINT("--> year=%ld, day-within-year=%ld",
			                     (long) year, (long) *out_day_within_year));
			DUK_ASSERT(*out_day_within_year >= 0);
			DUK_ASSERT(*out_day_within_year < (duk_bi_date_is_leap_year(year) ? 366 : 365));
			return year;
		}

		/* Note: this is very tricky; we must never 'overshoot' the
		 * correction downwards.
		 */
		year -= 1 + (diff_days - 1) / 366;  /* conservative */
	}
}

/* Given a (year, month, day-within-month) triple, compute day number.
 * The input triple is un-normalized and may contain non-finite values.
 */
DUK_LOCAL duk_double_t duk__make_day(duk_double_t year, duk_double_t month, duk_double_t day) {
	duk_int_t day_num;
	duk_bool_t is_leap;
	duk_small_int_t i, n;

	/* Assume that year, month, day are all coerced to whole numbers.
	 * They may also be NaN or infinity, in which case this function
	 * must return NaN or infinity to ensure time value becomes NaN.
	 * If 'day' is NaN, the final return will end up returning a NaN,
	 * so it doesn't need to be checked here.
	 */

	if (!DUK_ISFINITE(year) || !DUK_ISFINITE(month)) {
		return DUK_DOUBLE_NAN;
	}

	year += DUK_FLOOR(month / 12.0);

	month = DUK_FMOD(month, 12.0);
	if (month < 0.0) {
		/* handle negative values */
		month += 12.0;
	}

	/* The algorithm in E5.1 Section 15.9.1.12 normalizes month, but
	 * does not normalize the day-of-month (nor check whether or not
	 * it is finite) because it's not necessary for finding the day
	 * number which matches the (year,month) pair.
	 *
	 * We assume that duk__day_from_year() is exact here.
	 *
	 * Without an explicit infinity / NaN check in the beginning,
	 * day_num would be a bogus integer here.
	 *
	 * It's possible for 'year' to be out of integer range here.
	 * If so, we need to return NaN without integer overflow.
	 * This fixes test-bug-setyear-overflow.js.
	 */

	if (!duk_bi_date_year_in_valid_range(year)) {
		DUK_DD(DUK_DDPRINT("year not in ecmascript valid range, avoid integer overflow: %lf", (double) year));
		return DUK_DOUBLE_NAN;
	}
	day_num = duk__day_from_year((duk_int_t) year);
	is_leap = duk_bi_date_is_leap_year((duk_int_t) year);

	n = (duk_small_int_t) month;
	for (i = 0; i < n; i++) {
		day_num += duk__days_in_month[i];
		if (i == 1 && is_leap) {
			day_num++;
		}
	}

	/* If 'day' is NaN, returns NaN. */
	return (duk_double_t) day_num + day;
}

/* Split time value into parts.  The time value may contain fractions (it may
 * come from duk_time_to_components() API call) which are truncated.  Possible
 * local time adjustment has already been applied when reading the time value.
 */
DUK_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, duk_double_t *dparts, duk_small_uint_t flags) {
	duk_double_t d1, d2;
	duk_int_t t1, t2;
	duk_int_t day_since_epoch;
	duk_int_t year;  /* does not fit into 16 bits */
	duk_small_int_t day_in_year;
	duk_small_int_t month;
	duk_small_int_t day;
	duk_small_int_t dim;
	duk_int_t jan1_since_epoch;
	duk_small_int_t jan1_weekday;
	duk_int_t equiv_year;
	duk_small_uint_t i;
	duk_bool_t is_leap;
	duk_small_int_t arridx;

	DUK_ASSERT(DUK_ISFINITE(d));    /* caller checks */
	d = DUK_FLOOR(d);  /* remove fractions if present */
	DUK_ASSERT(DUK_FLOOR(d) == d);

	/* The timevalue must be in valid ECMAScript range, but since a local
	 * time offset can be applied, we need to allow a +/- 24h leeway to
	 * the value.  In other words, although the UTC time is within the
	 * ECMAScript range, the local part values can be just outside of it.
	 */
	DUK_UNREF(duk_bi_date_timeval_in_leeway_range);
	DUK_ASSERT(duk_bi_date_timeval_in_leeway_range(d));

	/* These computations are guaranteed to be exact for the valid
	 * E5 time value range, assuming milliseconds without fractions.
	 */
	d1 = (duk_double_t) DUK_FMOD(d, (double) DUK_DATE_MSEC_DAY);
	if (d1 < 0.0) {
		/* deal with negative values */
		d1 += (duk_double_t) DUK_DATE_MSEC_DAY;
	}
	d2 = DUK_FLOOR((double) (d / (duk_double_t) DUK_DATE_MSEC_DAY));
	DUK_ASSERT(d2 * ((duk_double_t) DUK_DATE_MSEC_DAY) + d1 == d);
	/* now expected to fit into a 32-bit integer */
	t1 = (duk_int_t) d1;
	t2 = (duk_int_t) d2;
	day_since_epoch = t2;
	DUK_ASSERT((duk_double_t) t1 == d1);
	DUK_ASSERT((duk_double_t) t2 == d2);

	/* t1 = milliseconds within day (fits 32 bit)
	 * t2 = day number from epoch (fits 32 bit, may be negative)
	 */

	parts[DUK_DATE_IDX_MILLISECOND] = t1 % 1000; t1 /= 1000;
	parts[DUK_DATE_IDX_SECOND] = t1 % 60; t1 /= 60;
	parts[DUK_DATE_IDX_MINUTE] = t1 % 60; t1 /= 60;
	parts[DUK_DATE_IDX_HOUR] = t1;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	if ((flags & DUK_DATE_FLAG_EQUIVYEAR) && (year < 1971 || year > 2037)) {
		DUK_ASSERT(is_leap == 0 || is_leap == 1);

		jan1_since_epoch = day_since_epoch - day_in_year;  /* day number for Jan 1 since epoch */
		DUK_ASSERT(jan1_since_epoch + DUK__WEEKDAY_MOD_ADDER >= 0);
		jan1_weekday = (jan1_since_epoch + 4 + DUK__WEEKDAY_MOD_ADDER) % 7;  /* E5.1 Section 15.9.1.6 */
		DUK_ASSERT(jan1_weekday >= 0 && jan1_weekday <= 6);
		arridx = jan1_weekday;
		if (is_leap) {
			arridx += 7;
		}
		DUK_ASSERT(arridx >= 0 && arridx < (duk_small_int_t) (sizeof(duk__date_equivyear) / sizeof(duk_uint8_t)));

		equiv_year = (duk_int_t) duk__date_equivyear[arridx] + 1970;
		year = equiv_year;
		DUK_DDD(DUK_DDDPRINT("equiv year mapping, year=%ld, day_in_year=%ld, day_since_epoch=%ld, "
		                     "jan1_since_epoch=%ld, jan1_weekday=%ld -> equiv year %ld",
		                     (long) year, (long) day_in_year, (long) day_since_epoch,
		                     (long) jan1_since_epoch, (long) jan1_weekday, (long) equiv_year));
	}

	parts[DUK_DATE_IDX_YEAR] = year;
	parts[DUK_DATE_IDX_MONTH] = month;
	parts[DUK_DATE_IDX_DAY] = day;

	if (flags & DUK_DATE_FLAG_ONEBASED) {
		parts[DUK_DATE_IDX_MONTH]++;  /* zero-based -> one-based */
		parts[DUK_DATE_IDX_DAY]++;    /* -""- */
	}

	if (dparts != NULL) {
		for (i = 0; i < DUK_DATE_IDX_NUM_PARTS; i++) {
			dparts[i] = (duk_double_t) parts[i];
		}
	}
}

/* Compute time value from (double) parts.  The parts can be either UTC
 * or local time; if local, they need to be (conceptually) converted into
 * UTC time.  The parts may represent valid or invalid time, and may be
 * wildly out of range (but may cancel each other and still come out in
 * the valid Date range).
 */
DUK_INTERNAL duk_double_t duk_bi_date_get_timeval_from_dparts(duk_double_t *dparts, duk_small_uint_t flags) {
#if defined(DUK_USE_PARANOID_DATE_COMPUTATION)
	/* See comments below on MakeTime why these are volatile. */
	volatile duk_double_t tmp_time;
	volatile duk_double_t tmp_day;
	volatile duk_double_t d;
#else
	duk_double_t tmp_time;
	duk_double_t tmp_day;
	duk_double_t d;
#endif
	duk_small_uint_t i;
	duk_int_t tzoff, tzoffprev1, tzoffprev2;

	/* Expects 'this' at top of stack on entry. */

	/* Coerce all finite parts with ToInteger().  ToInteger() must not
	 * be called for NaN/Infinity because it will convert e.g. NaN to
	 * zero.  If ToInteger() has already been called, this has no side
	 * effects and is idempotent.
	 *
	 * Don't read dparts[DUK_DATE_IDX_WEEKDAY]; it will cause Valgrind
	 * issues if the value is uninitialized.
	 */
	for (i = 0; i <= DUK_DATE_IDX_MILLISECOND; i++) {
		/* SCANBUILD: scan-build complains here about assigned value
		 * being garbage or undefined.  This is correct but operating
		 * on undefined values has no ill effect and is ignored by the
		 * caller in the case where this happens.
		 */
		d = dparts[i];
		if (DUK_ISFINITE(d)) {
			dparts[i] = duk_js_tointeger_number(d);
		}
	}

	/* Use explicit steps in computation to try to ensure that
	 * computation happens with intermediate results coerced to
	 * double values (instead of using something more accurate).
	 * E.g. E5.1 Section 15.9.1.11 requires use of IEEE 754
	 * rules (= ECMAScript '+' and '*' operators).
	 *
	 * Without 'volatile' even this approach fails on some platform
	 * and compiler combinations.  For instance, gcc 4.8.1 on Ubuntu
	 * 64-bit, with -m32 and without -std=c99, test-bi-date-canceling.js
	 * would fail because of some optimizations when computing tmp_time
	 * (MakeTime below).  Adding 'volatile' to tmp_time solved this
	 * particular problem (annoyingly, also adding debug prints or
	 * running the executable under valgrind hides it).
	 */

	/* MakeTime */
	tmp_time = 0.0;
	tmp_time += dparts[DUK_DATE_IDX_HOUR] * ((duk_double_t) DUK_DATE_MSEC_HOUR);
	tmp_time += dparts[DUK_DATE_IDX_MINUTE] * ((duk_double_t) DUK_DATE_MSEC_MINUTE);
	tmp_time += dparts[DUK_DATE_IDX_SECOND] * ((duk_double_t) DUK_DATE_MSEC_SECOND);
	tmp_time += dparts[DUK_DATE_IDX_MILLISECOND];

	/* MakeDay */
	tmp_day = duk__make_day(dparts[DUK_DATE_IDX_YEAR], dparts[DUK_DATE_IDX_MONTH], dparts[DUK_DATE_IDX_DAY]);

	/* MakeDate */
	d = tmp_day * ((duk_double_t) DUK_DATE_MSEC_DAY) + tmp_time;

	DUK_DDD(DUK_DDDPRINT("time=%lf day=%lf --> timeval=%lf",
	                     (double) tmp_time, (double) tmp_day, (double) d));

	/* Optional UTC conversion. */
	if (flags & DUK_DATE_FLAG_LOCALTIME) {
		/* DUK_USE_DATE_GET_LOCAL_TZOFFSET() needs to be called with a
		 * time value computed from UTC parts.  At this point we only
		 * have 'd' which is a time value computed from local parts, so
		 * it is off by the UTC-to-local time offset which we don't know
		 * yet.  The current solution for computing the UTC-to-local
		 * time offset is to iterate a few times and detect a fixed
		 * point or a two-cycle loop (or a sanity iteration limit),
		 * see test-bi-date-local-parts.js and test-bi-date-tzoffset-basic-fi.js.
		 *
		 * E5.1 Section 15.9.1.9:
		 * UTC(t) = t - LocalTZA - DaylightSavingTA(t - LocalTZA)
		 *
		 * For NaN/inf, DUK_USE_DATE_GET_LOCAL_TZOFFSET() returns 0.
		 */

#if 0
		/* Old solution: don't iterate, incorrect */
		tzoff = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d);
		DUK_DDD(DUK_DDDPRINT("tzoffset w/o iteration, tzoff=%ld", (long) tzoff));
		d -= tzoff * 1000L;
		DUK_UNREF(tzoffprev1);
		DUK_UNREF(tzoffprev2);
#endif

		/* Iteration solution */
		tzoff = 0;
		tzoffprev1 = 999999999L;  /* invalid value which never matches */
		for (i = 0; i < DUK__LOCAL_TZOFFSET_MAXITER; i++) {
			tzoffprev2 = tzoffprev1;
			tzoffprev1 = tzoff;
			tzoff = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d - tzoff * 1000L);
			DUK_DDD(DUK_DDDPRINT("tzoffset iteration, i=%d, tzoff=%ld, tzoffprev1=%ld tzoffprev2=%ld",
			                     (int) i, (long) tzoff, (long) tzoffprev1, (long) tzoffprev2));
			if (tzoff == tzoffprev1) {
				DUK_DDD(DUK_DDDPRINT("tzoffset iteration finished, i=%d, tzoff=%ld, tzoffprev1=%ld, tzoffprev2=%ld",
				                     (int) i, (long) tzoff, (long) tzoffprev1, (long) tzoffprev2));
				break;
			} else if (tzoff == tzoffprev2) {
				/* Two value cycle, see e.g. test-bi-date-tzoffset-basic-fi.js.
				 * In these cases, favor a higher tzoffset to get a consistent
				 * result which is independent of iteration count.  Not sure if
				 * this is a generically correct solution.
				 */
				DUK_DDD(DUK_DDDPRINT("tzoffset iteration two-value cycle, i=%d, tzoff=%ld, tzoffprev1=%ld, tzoffprev2=%ld",
				                     (int) i, (long) tzoff, (long) tzoffprev1, (long) tzoffprev2));
				if (tzoffprev1 > tzoff) {
					tzoff = tzoffprev1;
				}
				break;
			}
		}
		DUK_DDD(DUK_DDDPRINT("tzoffset iteration, tzoff=%ld", (long) tzoff));
		d -= tzoff * 1000L;
	}

	/* TimeClip(), which also handles Infinity -> NaN conversion */
	d = duk__timeclip(d);

	return d;
}

/*
 *  API oriented helpers
 */

/* Push 'this' binding, check that it is a Date object; then push the
 * internal time value.  At the end, stack is: [ ... this timeval ].
 * Returns the time value.  Local time adjustment is done if requested.
 */
DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_hthread *thr, duk_small_uint_t flags, duk_int_t *out_tzoffset) {
	duk_hobject *h;
	duk_double_t d;
	duk_int_t tzoffset = 0;

	duk_push_this(thr);
	h = duk_get_hobject(thr, -1);  /* XXX: getter with class check, useful in built-ins */
	if (h == NULL || DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_DATE) {
		DUK_ERROR_TYPE(thr, "expected Date");
		DUK_WO_NORETURN(return 0.0;);
	}

	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
	d = duk_to_number_m1(thr);
	duk_pop(thr);

	if (DUK_ISNAN(d)) {
		if (flags & DUK_DATE_FLAG_NAN_TO_ZERO) {
			d = 0.0;
		}
		if (flags & DUK_DATE_FLAG_NAN_TO_RANGE_ERROR) {
			DUK_ERROR_RANGE(thr, "Invalid Date");
			DUK_WO_NORETURN(return 0.0;);
		}
	}
	/* if no NaN handling flag, may still be NaN here, but not Inf */
	DUK_ASSERT(!DUK_ISINF(d));

	if (flags & DUK_DATE_FLAG_LOCALTIME) {
		/* Note: DST adjustment is determined using UTC time.
		 * If 'd' is NaN, tzoffset will be 0.
		 */
		tzoffset = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d);  /* seconds */
		d += tzoffset * 1000L;
	}
	if (out_tzoffset) {
		*out_tzoffset = tzoffset;
	}

	/* [ ... this ] */
	return d;
}

DUK_LOCAL duk_double_t duk__push_this_get_timeval(duk_hthread *thr, duk_small_uint_t flags) {
	return duk__push_this_get_timeval_tzoffset(thr, flags, NULL);
}

/* Set timeval to 'this' from dparts, push the new time value onto the
 * value stack and return 1 (caller can then tail call us).  Expects
 * the value stack to contain 'this' on the stack top.
 */
DUK_LOCAL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_double_t *dparts, duk_small_uint_t flags) {
	duk_double_t d;

	/* [ ... this ] */

	d = duk_bi_date_get_timeval_from_dparts(dparts, flags);
	duk_push_number(thr, d);  /* -> [ ... this timeval_new ] */
	duk_dup_top(thr);         /* -> [ ... this timeval_new timeval_new ] */
	duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE);

	/* stack top: new time value, return 1 to allow tail calls */
	return 1;
}

/* 'out_buf' must be at least DUK_BI_DATE_ISO8601_BUFSIZE long. */
DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags, duk_uint8_t *out_buf) {
	char yearstr[8];   /* "-123456\0" */
	char tzstr[8];     /* "+11:22\0" */
	char sep = (flags & DUK_DATE_FLAG_SEP_T) ? DUK_ASC_UC_T : DUK_ASC_SPACE;

	DUK_ASSERT(parts[DUK_DATE_IDX_MONTH] >= 1 && parts[DUK_DATE_IDX_MONTH] <= 12);
	DUK_ASSERT(parts[DUK_DATE_IDX_DAY] >= 1 && parts[DUK_DATE_IDX_DAY] <= 31);
	DUK_ASSERT(parts[DUK_DATE_IDX_YEAR] >= -999999 && parts[DUK_DATE_IDX_YEAR] <= 999999);

	/* Note: %06d for positive value, %07d for negative value to include
	 * sign and 6 digits.
	 */
	DUK_SNPRINTF(yearstr,
	             sizeof(yearstr),
	             (parts[DUK_DATE_IDX_YEAR] >= 0 && parts[DUK_DATE_IDX_YEAR] <= 9999) ? "%04ld" :
	                    ((parts[DUK_DATE_IDX_YEAR] >= 0) ? "+%06ld" : "%07ld"),
	             (long) parts[DUK_DATE_IDX_YEAR]);
	yearstr[sizeof(yearstr) - 1] = (char) 0;

	if (flags & DUK_DATE_FLAG_LOCALTIME) {
		/* tzoffset seconds are dropped; 16 bits suffice for
		 * time offset in minutes
		 */
		const char *fmt;
		duk_small_int_t tmp, arg_hours, arg_minutes;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	 * is shared.
	 */
	duk__format_parts_iso8601(parts, tzoffset, flags, buf);
	duk_push_string(thr, (const char *) buf);
	return 1;
}

/* Helper for component getter calls: check 'this' binding, get the
 * internal time value, split it into parts (either as UTC time or
 * local time), push a specified component as a return value to the
 * value stack and return 1 (caller can then tail call us).
 */
DUK_LOCAL duk_ret_t duk__get_part_helper(duk_hthread *thr, duk_small_uint_t flags_and_idx) {
	duk_double_t d;
	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
	duk_small_uint_t idx_part = (duk_small_uint_t) (flags_and_idx >> DUK_DATE_FLAG_VALUE_SHIFT);  /* unpack args */

	DUK_ASSERT_DISABLE(idx_part >= 0);  /* unsigned */
	DUK_ASSERT(idx_part < DUK_DATE_IDX_NUM_PARTS);

	d = duk__push_this_get_timeval(thr, flags_and_idx);
	if (DUK_ISNAN(d)) {
		duk_push_nan(thr);
		return 1;
	}
	DUK_ASSERT(DUK_ISFINITE(d));

	duk_bi_date_timeval_to_parts(d, parts, NULL, flags_and_idx);  /* no need to mask idx portion */

	/* Setter APIs detect special year numbers (0...99) and apply a +1900
	 * only in certain cases.  The legacy getYear() getter applies -1900
	 * unconditionally.
	 */
	duk_push_int(thr, (flags_and_idx & DUK_DATE_FLAG_SUB1900) ? parts[idx_part] - 1900 : parts[idx_part]);
	return 1;
}

/* Helper for component setter calls: check 'this' binding, get the
 * internal time value, split it into parts (either as UTC time or
 * local time), modify one or more components as specified, recompute
 * the time value, set it as the internal value.  Finally, push the
 * new time value as a return value to the value stack and return 1
 * (caller can then tail call us).
 */
DUK_LOCAL duk_ret_t duk__set_part_helper(duk_hthread *thr, duk_small_uint_t flags_and_maxnargs) {
	duk_double_t d;
	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
	duk_idx_t nargs;
	duk_small_uint_t maxnargs = (duk_small_uint_t) (flags_and_maxnargs >> DUK_DATE_FLAG_VALUE_SHIFT);  /* unpack args */
	duk_small_uint_t idx_first, idx;
	duk_small_uint_t i;

	nargs = duk_get_top(thr);
	d = duk__push_this_get_timeval(thr, flags_and_maxnargs);
	DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d));

	if (DUK_ISFINITE(d)) {
		duk_bi_date_timeval_to_parts(d, parts, dparts, flags_and_maxnargs);
	} else {
		/* NaN timevalue: we need to coerce the arguments, but
		 * the resulting internal timestamp needs to remain NaN.
		 * This works but is not pretty: parts and dparts will
		 * be partially uninitialized, but we only write to them.
		 */
	}

	/*
	 *  Determining which datetime components to overwrite based on
	 *  stack arguments is a bit complicated, but important to factor
	 *  out from setters themselves for compactness.
	 *
	 *  If DUK_DATE_FLAG_TIMESETTER, maxnargs indicates setter type:
	 *
	 *   1 -> millisecond
	 *   2 -> second, [millisecond]
	 *   3 -> minute, [second], [millisecond]
	 *   4 -> hour, [minute], [second], [millisecond]
	 *
	 *  Else:
	 *
	 *   1 -> date
	 *   2 -> month, [date]
	 *   3 -> year, [month], [date]
	 *
	 *  By comparing nargs and maxnargs (and flags) we know which
	 *  components to override.  We rely on part index ordering.
	 */

	if (flags_and_maxnargs & DUK_DATE_FLAG_TIMESETTER) {
		DUK_ASSERT(maxnargs >= 1 && maxnargs <= 4);
		idx_first = DUK_DATE_IDX_MILLISECOND - (maxnargs - 1);
	} else {
		DUK_ASSERT(maxnargs >= 1 && maxnargs <= 3);
		idx_first = DUK_DATE_IDX_DAY - (maxnargs - 1);
	}
	DUK_ASSERT_DISABLE(idx_first >= 0);  /* unsigned */
	DUK_ASSERT(idx_first < DUK_DATE_IDX_NUM_PARTS);

	for (i = 0; i < maxnargs; i++) {
		if ((duk_idx_t) i >= nargs) {
			/* no argument given -> leave components untouched */
			break;
		}
		idx = idx_first + i;
		DUK_ASSERT_DISABLE(idx >= 0);  /* unsigned */
		DUK_ASSERT(idx < DUK_DATE_IDX_NUM_PARTS);

		if (idx == DUK_DATE_IDX_YEAR && (flags_and_maxnargs & DUK_DATE_FLAG_YEAR_FIXUP)) {
			duk__twodigit_year_fixup(thr, (duk_idx_t) i);
		}

		dparts[idx] = duk_to_number(thr, (duk_idx_t) i);

		if (idx == DUK_DATE_IDX_DAY) {
			/* Day-of-month is one-based in the API, but zero-based
			 * internally, so fix here.  Note that month is zero-based
			 * both in the API and internally.
			 */
			/* SCANBUILD: complains about use of uninitialized values.
			 * The complaint is correct, but operating in undefined
			 * values here is intentional in some cases and the caller
			 * ignores the results.
			 */
			dparts[idx] -= 1.0;
		}
	}

	/* Leaves new timevalue on stack top and returns 1, which is correct
	 * for part setters.
	 */
	if (DUK_ISFINITE(d)) {
		return duk__set_this_timeval_from_dparts(thr, dparts, flags_and_maxnargs);
	} else {
		/* Internal timevalue is already NaN, so don't touch it. */
		duk_push_nan(thr);
		return 1;
	}
}

/* Apply ToNumber() to specified index; if ToInteger(val) in [0,99], add
 * 1900 and replace value at idx_val.
 */
DUK_LOCAL void duk__twodigit_year_fixup(duk_hthread *thr, duk_idx_t idx_val) {
	duk_double_t d;

	/* XXX: idx_val would fit into 16 bits, but using duk_small_uint_t
	 * might not generate better code due to casting.
	 */

	/* E5 Sections 15.9.3.1, B.2.4, B.2.5 */
	duk_to_number(thr, idx_val);
	if (duk_is_nan(thr, idx_val)) {
		return;
	}
	duk_dup(thr, idx_val);
	duk_to_int(thr, -1);
	d = duk_get_number(thr, -1);  /* get as double to handle huge numbers correctly */
	if (d >= 0.0 && d <= 99.0) {
		d += 1900.0;
		duk_push_number(thr, d);
		duk_replace(thr, idx_val);
	}
	duk_pop(thr);
}

/* Set datetime parts from stack arguments, defaulting any missing values.
 * Day-of-week is not set; it is not required when setting the time value.
 */
DUK_LOCAL void duk__set_parts_from_args(duk_hthread *thr, duk_double_t *dparts, duk_idx_t nargs) {
	duk_double_t d;
	duk_small_uint_t i;
	duk_small_uint_t idx;

	/* Causes a ToNumber() coercion, but doesn't break coercion order since
	 * year is coerced first anyway.
	 */
	duk__twodigit_year_fixup(thr, 0);

	/* There are at most 7 args, but we use 8 here so that also
	 * DUK_DATE_IDX_WEEKDAY gets initialized (to zero) to avoid the potential
	 * for any Valgrind gripes later.
	 */
	for (i = 0; i < 8; i++) {
		/* Note: rely on index ordering */
		idx = DUK_DATE_IDX_YEAR + i;
		if ((duk_idx_t) i < nargs) {
			d = duk_to_number(thr, (duk_idx_t) i);
			if (idx == DUK_DATE_IDX_DAY) {
				/* Convert day from one-based to zero-based (internal).  This may
				 * cause the day part to be negative, which is OK.
				 */
				d -= 1.0;
			}
		} else {

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	/* 35: setUTCMonth */
	0 + (2 << DUK_DATE_FLAG_VALUE_SHIFT),

	/* 36: setFullYear */
	DUK_DATE_FLAG_NAN_TO_ZERO + DUK_DATE_FLAG_LOCALTIME + (3 << DUK_DATE_FLAG_VALUE_SHIFT),

	/* 37: setUTCFullYear */
	DUK_DATE_FLAG_NAN_TO_ZERO + (3 << DUK_DATE_FLAG_VALUE_SHIFT),

	/* 38: getYear */
	DUK_DATE_FLAG_LOCALTIME + DUK_DATE_FLAG_SUB1900 + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT),

	/* 39: setYear */
	DUK_DATE_FLAG_NAN_TO_ZERO + DUK_DATE_FLAG_YEAR_FIXUP + (3 << DUK_DATE_FLAG_VALUE_SHIFT),
};

DUK_LOCAL duk_small_uint_t duk__date_get_indirect_magic(duk_hthread *thr) {
	duk_small_uint_t magicidx = (duk_small_uint_t) duk_get_current_magic(thr);
	DUK_ASSERT(magicidx < (duk_small_int_t) (sizeof(duk__date_magics) / sizeof(duk_uint16_t)));
	return (duk_small_uint_t) duk__date_magics[magicidx];
}

#if defined(DUK_USE_DATE_BUILTIN)
/*
 *  Constructor calls
 */

DUK_INTERNAL duk_ret_t duk_bi_date_constructor(duk_hthread *thr) {
	duk_idx_t nargs = duk_get_top(thr);
	duk_bool_t is_cons = duk_is_constructor_call(thr);
	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
	duk_double_t d;

	DUK_DDD(DUK_DDDPRINT("Date constructor, nargs=%ld, is_cons=%ld", (long) nargs, (long) is_cons));

	(void) duk_push_object_helper(thr,
	                              DUK_HOBJECT_FLAG_EXTENSIBLE |
	                              DUK_HOBJECT_FLAG_FASTREFS |
	                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATE),
	                              DUK_BIDX_DATE_PROTOTYPE);

	/* Unlike most built-ins, the internal [[PrimitiveValue]] of a Date
	 * is mutable.
	 */

	if (nargs == 0 || !is_cons) {
		d = duk__timeclip(duk_time_get_ecmascript_time_nofrac(thr));
		duk_push_number(thr, d);
		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W);
		if (!is_cons) {
			/* called as a normal function: return new Date().toString() */
			duk_to_string(thr, -1);
		}
		return 1;
	} else if (nargs == 1) {
		const char *str;
		duk_to_primitive(thr, 0, DUK_HINT_NONE);
		str = duk_get_string_notsymbol(thr, 0);
		if (str) {
			duk__parse_string(thr, str);
			duk_replace(thr, 0);  /* may be NaN */
		}
		d = duk__timeclip(duk_to_number(thr, 0));  /* symbols fail here */
		duk_push_number(thr, d);
		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W);
		return 1;
	}

	duk__set_parts_from_args(thr, dparts, nargs);

	/* Parts are in local time, convert when setting. */

	(void) duk__set_this_timeval_from_dparts(thr, dparts, DUK_DATE_FLAG_LOCALTIME /*flags*/);  /* -> [ ... this timeval ] */
	duk_pop(thr);  /* -> [ ... this ] */
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_date_constructor_parse(duk_hthread *thr) {
	return duk__parse_string(thr, duk_to_string(thr, 0));
}

DUK_INTERNAL duk_ret_t duk_bi_date_constructor_utc(duk_hthread *thr) {
	duk_idx_t nargs = duk_get_top(thr);
	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
	duk_double_t d;

	/* Behavior for nargs < 2 is implementation dependent: currently we'll
	 * set a NaN time value (matching V8 behavior) in this case.
	 */

	if (nargs < 2) {
		duk_push_nan(thr);
	} else {
		duk__set_parts_from_args(thr, dparts, nargs);
		d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/);
		duk_push_number(thr, d);
	}
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_hthread *thr) {
	duk_double_t d;

	d = duk_time_get_ecmascript_time_nofrac(thr);
	DUK_ASSERT(duk__timeclip(d) == d);  /* TimeClip() should never be necessary */
	duk_push_number(thr, d);
	return 1;
}

/*
 *  String/JSON conversions
 *
 *  Human readable conversions are now basically ISO 8601 with a space
 *  (instead of 'T') as the date/time separator.  This is a good baseline
 *  and is platform independent.
 *
 *  A shared native helper to provide many conversions.  Magic value contains
 *  a set of flags.  The helper provides:
 *
 *    toString()
 *    toDateString()
 *    toTimeString()
 *    toLocaleString()
 *    toLocaleDateString()
 *    toLocaleTimeString()
 *    toUTCString()
 *    toISOString()
 *
 *  Notes:
 *
 *    - Date.prototype.toGMTString() and Date.prototype.toUTCString() are
 *      required to be the same ECMAScript function object (!), so it is
 *      omitted from here.
 *
 *    - Date.prototype.toUTCString(): E5.1 specification does not require a
 *      specific format, but result should be human readable.  The
 *      specification suggests using ISO 8601 format with a space (instead
 *      of 'T') separator if a more human readable format is not available.
 *
 *    - Date.prototype.toISOString(): unlike other conversion functions,
 *      toISOString() requires a RangeError for invalid date values.
 */

DUK_INTERNAL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_hthread *thr) {
	duk_small_uint_t flags = duk__date_get_indirect_magic(thr);
	return duk__to_string_helper(thr, flags);
}

DUK_INTERNAL duk_ret_t duk_bi_date_prototype_value_of(duk_hthread *thr) {
	/* This native function is also used for Date.prototype.getTime()
	 * as their behavior is identical.
	 */

	duk_double_t d = duk__push_this_get_timeval(thr, 0 /*flags*/);  /* -> [ this ] */
	DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d));
	duk_push_number(thr, d);
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_hthread *thr) {
	/* Note: toJSON() is a generic function which works even if 'this'
	 * is not a Date.  The sole argument is ignored.
	 */

	duk_push_this(thr);
	duk_to_object(thr, -1);

	duk_dup_top(thr);
	duk_to_primitive(thr, -1, DUK_HINT_NUMBER);
	if (duk_is_number(thr, -1)) {
		duk_double_t d = duk_get_number(thr, -1);
		if (!DUK_ISFINITE(d)) {
			duk_push_null(thr);
			return 1;
		}
	}
	duk_pop(thr);

	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_ISO_STRING);
	duk_dup_m2(thr);  /* -> [ O toIsoString O ] */
	duk_call_method(thr, 0);
	return 1;
}

/*
 *  Getters.
 *
 *  Implementing getters is quite easy.  The internal time value is either
 *  NaN, or represents milliseconds (without fractions) from Jan 1, 1970.
 *  The internal time value can be converted to integer parts, and each
 *  part will be normalized and will fit into a 32-bit signed integer.
 *
 *  A shared native helper to provide all getters.  Magic value contains
 *  a set of flags and also packs the date component index argument.  The
 *  helper provides:
 *
 *    getFullYear()
 *    getUTCFullYear()
 *    getMonth()
 *    getUTCMonth()
 *    getDate()
 *    getUTCDate()
 *    getDay()
 *    getUTCDay()
 *    getHours()
 *    getUTCHours()
 *    getMinutes()
 *    getUTCMinutes()
 *    getSeconds()
 *    getUTCSeconds()
 *    getMilliseconds()
 *    getUTCMilliseconds()
 *    getYear()
 *
 *  Notes:
 *
 *    - Date.prototype.getDate(): 'date' means day-of-month, and is
 *      zero-based in internal calculations but public API expects it to
 *      be one-based.
 *
 *    - Date.prototype.getTime() and Date.prototype.valueOf() have identical
 *      behavior.  They have separate function objects, but share the same C
 *      function (duk_bi_date_prototype_value_of).
 */

DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_shared(duk_hthread *thr) {
	duk_small_uint_t flags_and_idx = duk__date_get_indirect_magic(thr);
	return duk__get_part_helper(thr, flags_and_idx);
}

DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_hthread *thr) {
	/*
	 *  Return (t - LocalTime(t)) in minutes:
	 *
	 *    t - LocalTime(t) = t - (t + LocalTZA + DaylightSavingTA(t))
	 *                     = -(LocalTZA + DaylightSavingTA(t))
	 *
	 *  where DaylightSavingTA() is checked for time 't'.
	 *
	 *  Note that the sign of the result is opposite to common usage,
	 *  e.g. for EE(S)T which normally is +2h or +3h from UTC, this
	 *  function returns -120 or -180.
	 *
	 */

	duk_double_t d;
	duk_int_t tzoffset;

	/* Note: DST adjustment is determined using UTC time. */
	d = duk__push_this_get_timeval(thr, 0 /*flags*/);
	DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d));
	if (DUK_ISNAN(d)) {
		duk_push_nan(thr);
	} else {
		DUK_ASSERT(DUK_ISFINITE(d));
		tzoffset = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d);
		duk_push_int(thr, -tzoffset / 60);
	}
	return 1;
}

/*
 *  Setters.
 *
 *  Setters are a bit more complicated than getters.  Component setters
 *  break down the current time value into its (normalized) component
 *  parts, replace one or more components with -unnormalized- new values,
 *  and the components are then converted back into a time value.  As an
 *  example of using unnormalized values:
 *
 *    var d = new Date(1234567890);
 *
 *  is equivalent to:
 *
 *    var d = new Date(0);
 *    d.setUTCMilliseconds(1234567890);
 *
 *  A shared native helper to provide almost all setters.  Magic value
 *  contains a set of flags and also packs the "maxnargs" argument.  The
 *  helper provides:
 *
 *    setMilliseconds()
 *    setUTCMilliseconds()
 *    setSeconds()
 *    setUTCSeconds()
 *    setMinutes()
 *    setUTCMinutes()
 *    setHours()
 *    setUTCHours()
 *    setDate()
 *    setUTCDate()
 *    setMonth()
 *    setUTCMonth()
 *    setFullYear()
 *    setUTCFullYear()
 *    setYear()
 *
 *  Notes:
 *
 *    - Date.prototype.setYear() (Section B addition): special year check
 *      is omitted.  NaN / Infinity will just flow through and ultimately
 *      result in a NaN internal time value.
 *
 *    - Date.prototype.setYear() does not have optional arguments for
 *      setting month and day-in-month (like setFullYear()), but we indicate
 *      'maxnargs' to be 3 to get the year written to the correct component
 *      index in duk__set_part_helper().  The function has nargs == 1, so only
 *      the year will be set regardless of actual argument count.
 */

DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_shared(duk_hthread *thr) {
	duk_small_uint_t flags_and_maxnargs = duk__date_get_indirect_magic(thr);
	return duk__set_part_helper(thr, flags_and_maxnargs);
}

DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_hthread *thr) {
	duk_double_t d;

	(void) duk__push_this_get_timeval(thr, 0 /*flags*/); /* -> [ timeval this ] */
	d = duk__timeclip(duk_to_number(thr, 0));
	duk_push_number(thr, d);
	duk_dup_top(thr);
	duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE); /* -> [ timeval this timeval ] */

	return 1;
}

/*
 *  Misc.
 */

#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_date_prototype_toprimitive(duk_hthread *thr) {
	duk_size_t hintlen;
	const char *hintstr;
	duk_int_t hint;

	/* Invokes OrdinaryToPrimitive() with suitable hint.  Note that the
	 * method is generic, and works on non-Date arguments too.
	 *
	 * https://www.ecma-international.org/ecma-262/6.0/#sec-date.prototype-@@toprimitive
	 */

	duk_push_this(thr);
	duk_require_object(thr, -1);
	DUK_ASSERT_TOP(thr, 2);

	hintstr = duk_require_lstring(thr, 0, &hintlen);
	if ((hintlen == 6 && DUK_STRCMP(hintstr, "string") == 0) ||
	    (hintlen == 7 && DUK_STRCMP(hintstr, "default") == 0)) {
		hint = DUK_HINT_STRING;
	} else if (hintlen == 6 && DUK_STRCMP(hintstr, "number") == 0) {
		hint = DUK_HINT_NUMBER;
	} else {
		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
	}

	duk_to_primitive_ordinary(thr, -1, hint);
	return 1;
}
#endif  /* DUK_USE_SYMBOL_BUILTIN */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

/*
 *  Unix-like Date providers
 *
 *  Generally useful Unix / POSIX / ANSI Date providers.
 */

/* #include duk_internal.h -> already included */

/* The necessary #includes are in place in duk_config.h. */

/* Buffer sizes for some UNIX calls.  Larger than strictly necessary
 * to avoid Valgrind errors.
 */
#define DUK__STRPTIME_BUF_SIZE  64
#define DUK__STRFTIME_BUF_SIZE  64

#if defined(DUK_USE_DATE_NOW_GETTIMEOFDAY)
/* Get current ECMAScript time (= UNIX/Posix time, but in milliseconds). */
DUK_INTERNAL duk_double_t duk_bi_date_get_now_gettimeofday(void) {
	struct timeval tv;
	duk_double_t d;

	if (gettimeofday(&tv, NULL) != 0) {
		DUK_D(DUK_DPRINT("gettimeofday() failed"));
		return 0.0;
	}

	/* As of Duktape 2.2.0 allow fractions. */
	d = ((duk_double_t) tv.tv_sec) * 1000.0 +
	    ((duk_double_t) tv.tv_usec) / 1000.0;

	return d;
}
#endif  /* DUK_USE_DATE_NOW_GETTIMEOFDAY */

#if defined(DUK_USE_DATE_NOW_TIME)
/* Not a very good provider: only full seconds are available. */
DUK_INTERNAL duk_double_t duk_bi_date_get_now_time(void) {
	time_t t;

	t = time(NULL);
	if (t == (time_t) -1) {
		DUK_D(DUK_DPRINT("time() failed"));
		return 0.0;
	}
	return ((duk_double_t) t) * 1000.0;
}
#endif  /* DUK_USE_DATE_NOW_TIME */

#if defined(DUK_USE_DATE_TZO_GMTIME) || defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S)
/* Get local time offset (in seconds) for a certain (UTC) instant 'd'. */
DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) {
	time_t t, t1, t2;
	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
	struct tm tms[2];
#if defined(DUK_USE_DATE_TZO_GMTIME)
	struct tm *tm_ptr;
#endif

	/* For NaN/inf, the return value doesn't matter. */
	if (!DUK_ISFINITE(d)) {
		return 0;
	}

	/* If not within ECMAScript range, some integer time calculations
	 * won't work correctly (and some asserts will fail), so bail out
	 * if so.  This fixes test-bug-date-insane-setyear.js.  There is
	 * a +/- 24h leeway in this range check to avoid a test262 corner
	 * case documented in test-bug-date-timeval-edges.js.
	 */
	if (!duk_bi_date_timeval_in_leeway_range(d)) {
		DUK_DD(DUK_DDPRINT("timeval not within valid range, skip tzoffset computation to avoid integer overflows"));
		return 0;
	}

	/*
	 *  This is a bit tricky to implement portably.  The result depends
	 *  on the timestamp (specifically, DST depends on the timestamp).
	 *  If e.g. UNIX APIs are used, they'll have portability issues with
	 *  very small and very large years.
	 *
	 *  Current approach:
	 *
	 *  - Stay within portable UNIX limits by using equivalent year mapping.
	 *    Avoid year 1970 and 2038 as some conversions start to fail, at
	 *    least on some platforms.  Avoiding 1970 means that there are
	 *    currently DST discrepancies for 1970.
	 *
	 *  - Create a UTC and local time breakdowns from 't'.  Then create
	 *    a time_t using gmtime() and localtime() and compute the time
	 *    difference between the two.
	 *
	 *  Equivalent year mapping (E5 Section 15.9.1.8):
	 *
	 *    If the host environment provides functionality for determining
	 *    daylight saving time, the implementation of ECMAScript is free
	 *    to map the year in question to an equivalent year (same
	 *    leap-year-ness and same starting week day for the year) for which
	 *    the host environment provides daylight saving time information.
	 *    The only restriction is that all equivalent years should produce
	 *    the same result.
	 *
	 *  This approach is quite reasonable but not entirely correct, e.g.
	 *  the specification also states (E5 Section 15.9.1.8):
	 *
	 *    The implementation of ECMAScript should not try to determine
	 *    whether the exact time was subject to daylight saving time, but
	 *    just whether daylight saving time would have been in effect if
	 *    the _current daylight saving time algorithm_ had been used at the
	 *    time.  This avoids complications such as taking into account the
	 *    years that the locale observed daylight saving time year round.
	 *
	 *  Since we rely on the platform APIs for conversions between local
	 *  time and UTC, we can't guarantee the above.  Rather, if the platform
	 *  has historical DST rules they will be applied.  This seems to be the
	 *  general preferred direction in ECMAScript standardization (or at least
	 *  implementations) anyway, and even the equivalent year mapping should
	 *  be disabled if the platform is known to handle DST properly for the
	 *  full ECMAScript range.
	 *

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

#endif  /* DUK_USE_JX */

#if defined(DUK_USE_JX)
DUK_LOCAL void duk__dec_buffer(duk_json_dec_ctx *js_ctx) {
	duk_hthread *thr = js_ctx->thr;
	const duk_uint8_t *p;
	duk_uint8_t *buf;
	duk_size_t src_len;
	duk_small_int_t x;

	/* Caller has already eaten the first character ('|') which we don't need. */

	p = js_ctx->p;

	/* XXX: Would be nice to share the fast path loop from duk_hex_decode()
	 * and avoid creating a temporary buffer.  However, there are some
	 * differences which prevent trivial sharing:
	 *
	 *   - Pipe char detection
	 *   - EOF detection
	 *   - Unknown length of input and output
	 *
	 * The best approach here would be a bufwriter and a reasonaly sized
	 * safe inner loop (e.g. 64 output bytes at a time).
	 */

	for (;;) {
		x = *p;

		/* This loop intentionally does not ensure characters are valid
		 * ([0-9a-fA-F]) because the hex decode call below will do that.
		 */
		if (x == DUK_ASC_PIPE) {
			break;
		} else if (x <= 0) {
			/* NUL term or -1 (EOF), NUL check would suffice */
			goto syntax_error;
		}
		p++;
	}

	/* XXX: this is not very nice; unnecessary copy is made. */
	src_len = (duk_size_t) (p - js_ctx->p);
	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_len);
	DUK_ASSERT(buf != NULL);
	duk_memcpy((void *) buf, (const void *) js_ctx->p, src_len);
	duk_hex_decode(thr, -1);

	js_ctx->p = p + 1;  /* skip '|' */

	/* [ ... buf ] */

	return;

 syntax_error:
	duk__dec_syntax_error(js_ctx);
	DUK_UNREACHABLE();
}
#endif  /* DUK_USE_JX */

/* Parse a number, other than NaN or +/- Infinity */
DUK_LOCAL void duk__dec_number(duk_json_dec_ctx *js_ctx) {
	duk_hthread *thr = js_ctx->thr;
	const duk_uint8_t *p_start;
	const duk_uint8_t *p;
	duk_uint8_t x;
	duk_small_uint_t s2n_flags;

	DUK_DDD(DUK_DDDPRINT("parse_number"));

	p_start = js_ctx->p;

	/* First pass parse is very lenient (e.g. allows '1.2.3') and extracts a
	 * string for strict number parsing.
	 */

	p = js_ctx->p;
	for (;;) {
		x = *p;

		DUK_DDD(DUK_DDDPRINT("parse_number: p_start=%p, p=%p, p_end=%p, x=%ld",
		                     (const void *) p_start, (const void *) p,
		                     (const void *) js_ctx->p_end, (long) x));

#if defined(DUK_USE_JSON_DECNUMBER_FASTPATH)
		/* This fast path is pretty marginal in practice.
		 * XXX: candidate for removal.
		 */
		DUK_ASSERT(duk__json_decnumber_lookup[0x00] == 0x00);  /* end-of-input breaks */
		if (duk__json_decnumber_lookup[x] == 0) {
			break;
		}
#else  /* DUK_USE_JSON_DECNUMBER_FASTPATH */
		if (!((x >= DUK_ASC_0 && x <= DUK_ASC_9) ||
		      (x == DUK_ASC_PERIOD || x == DUK_ASC_LC_E ||
		       x == DUK_ASC_UC_E || x == DUK_ASC_MINUS || x == DUK_ASC_PLUS))) {
			/* Plus sign must be accepted for positive exponents
			 * (e.g. '1.5e+2').  This clause catches NULs.
			 */
			break;
		}
#endif  /* DUK_USE_JSON_DECNUMBER_FASTPATH */
		p++;  /* safe, because matched (NUL causes a break) */
	}
	js_ctx->p = p;

	DUK_ASSERT(js_ctx->p > p_start);
	duk_push_lstring(thr, (const char *) p_start, (duk_size_t) (p - p_start));

	s2n_flags = DUK_S2N_FLAG_ALLOW_EXP |
	            DUK_S2N_FLAG_ALLOW_MINUS |  /* but don't allow leading plus */
	            DUK_S2N_FLAG_ALLOW_FRAC;

	DUK_DDD(DUK_DDDPRINT("parse_number: string before parsing: %!T",
	                     (duk_tval *) duk_get_tval(thr, -1)));
	duk_numconv_parse(thr, 10 /*radix*/, s2n_flags);
	if (duk_is_nan(thr, -1)) {
		duk__dec_syntax_error(js_ctx);
	}
	DUK_ASSERT(duk_is_number(thr, -1));
	DUK_DDD(DUK_DDDPRINT("parse_number: final number: %!T",

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

			goto abort_fastpath;
		}
#endif

#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
		if (js_ctx->flag_ext_custom_or_compatible) {
			duk__enc_buffer_jx_jc(js_ctx, DUK_TVAL_GET_BUFFER(tv));
			break;
		}
#endif

		/* Plain buffers mimic Uint8Arrays, and have enumerable index
		 * properties.
		 */
		duk__enc_buffer_json_fastpath(js_ctx, DUK_TVAL_GET_BUFFER(tv));
		break;
	}
	case DUK_TAG_POINTER: {
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
		if (js_ctx->flag_ext_custom_or_compatible) {
			duk__enc_pointer(js_ctx, DUK_TVAL_GET_POINTER(tv));
			break;
		} else {
			goto emit_undefined;
		}
#else
		goto emit_undefined;
#endif
	}
	case DUK_TAG_LIGHTFUNC: {
		/* A lightfunc might also inherit a .toJSON() so just bail out. */
		/* XXX: Could just lookup .toJSON() and continue in fast path,
		 * as it would almost never be defined.
		 */
		DUK_DD(DUK_DDPRINT("value is a lightfunc, abort fast path"));
		goto abort_fastpath;
	}
#if defined(DUK_USE_FASTINT)
	case DUK_TAG_FASTINT: {
		/* Number serialization has a significant impact relative to
		 * other fast path code, so careful fast path for fastints.
		 */
		duk__enc_fastint_tval(js_ctx, tv);
		break;
	}
#endif
	default: {
		/* XXX: A fast path for usual integers would be useful when
		 * fastint support is not enabled.
		 */
		DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));

		/* XXX: Stack discipline is annoying, could be changed in numconv. */
		duk_push_tval(js_ctx->thr, tv);
		duk__enc_double(js_ctx);
		duk_pop(js_ctx->thr);

#if 0
		/* Could also rely on native sprintf(), but it will handle
		 * values like NaN, Infinity, -0, exponent notation etc in
		 * a JSON-incompatible way.
		 */
		duk_double_t d;
		char buf[64];

		DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
		d = DUK_TVAL_GET_DOUBLE(tv);
		DUK_SPRINTF(buf, "%lg", d);
		DUK__EMIT_CSTR(js_ctx, buf);
#endif
	}
	}
	return 1;  /* not undefined */

 emit_undefined:
	return 0;  /* value was undefined/unsupported */

 abort_fastpath:
	/* Error message doesn't matter: the error is ignored anyway. */
	DUK_DD(DUK_DDPRINT("aborting fast path"));
	DUK_ERROR_INTERNAL(js_ctx->thr);
	DUK_WO_NORETURN(return 0;);
}

DUK_LOCAL duk_ret_t duk__json_stringify_fast(duk_hthread *thr, void *udata) {
	duk_json_enc_ctx *js_ctx;
	duk_tval *tv;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(udata != NULL);

	js_ctx = (duk_json_enc_ctx *) udata;
	DUK_ASSERT(js_ctx != NULL);

	tv = DUK_GET_TVAL_NEGIDX(thr, -1);
	if (duk__json_stringify_fast_value(js_ctx, tv) == 0) {
		DUK_DD(DUK_DDPRINT("top level value not supported, fail fast path"));
		DUK_DCERROR_TYPE_INVALID_ARGS(thr);  /* Error message is ignored, so doesn't matter. */
	}

	return 0;
}
#endif  /* DUK_USE_JSON_STRINGIFY_FASTPATH */

/*
 *  Top level wrappers
 */

DUK_INTERNAL
void duk_bi_json_parse_helper(duk_hthread *thr,
                              duk_idx_t idx_value,
                              duk_idx_t idx_reviver,
                              duk_small_uint_t flags) {
	duk_json_dec_ctx js_ctx_alloc;
	duk_json_dec_ctx *js_ctx = &js_ctx_alloc;
	duk_hstring *h_text;
#if defined(DUK_USE_ASSERTIONS)
	duk_idx_t entry_top = duk_get_top(thr);
#endif

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

			 * specification is not very clear on whether that is required.
			 */

			duk_uarridx_t plist_idx = 0;
			duk_small_uint_t enum_flags;

			js_ctx->idx_proplist = duk_push_array(thr);  /* XXX: array internal? */

			enum_flags = DUK_ENUM_ARRAY_INDICES_ONLY |
			             DUK_ENUM_SORT_ARRAY_INDICES;  /* expensive flag */
			duk_enum(thr, idx_replacer, enum_flags);
			while (duk_next(thr, -1 /*enum_index*/, 1 /*get_value*/)) {
				/* [ ... proplist enum_obj key val ] */
				if (duk__enc_allow_into_proplist(duk_get_tval(thr, -1))) {
					/* XXX: duplicates should be eliminated here */
					DUK_DDD(DUK_DDDPRINT("proplist enum: key=%!T, val=%!T --> accept",
					                     (duk_tval *) duk_get_tval(thr, -2),
					                     (duk_tval *) duk_get_tval(thr, -1)));
					duk_to_string(thr, -1);  /* extra coercion of strings is OK */
					duk_put_prop_index(thr, -4, plist_idx);  /* -> [ ... proplist enum_obj key ] */
					plist_idx++;
					duk_pop(thr);
				} else {
					DUK_DDD(DUK_DDDPRINT("proplist enum: key=%!T, val=%!T --> reject",
					                     (duk_tval *) duk_get_tval(thr, -2),
					                     (duk_tval *) duk_get_tval(thr, -1)));
					duk_pop_2(thr);
				}
                        }
                        duk_pop(thr);  /* pop enum */

			/* [ ... proplist ] */
		}
	}

	/* [ ... buf loop (proplist) ] */

	/*
	 *  Process space (3rd argument to JSON.stringify)
	 */

	h = duk_get_hobject(thr, idx_space);
	if (h != NULL) {
		duk_small_uint_t c = DUK_HOBJECT_GET_CLASS_NUMBER(h);
		if (c == DUK_HOBJECT_CLASS_NUMBER) {
			duk_to_number(thr, idx_space);
		} else if (c == DUK_HOBJECT_CLASS_STRING) {
			duk_to_string(thr, idx_space);
		}
	}

	if (duk_is_number(thr, idx_space)) {
		duk_small_int_t nspace;
		/* spaces[] must be static to allow initializer with old compilers like BCC */
		static const char spaces[10] = {
			DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE,
			DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE,
			DUK_ASC_SPACE, DUK_ASC_SPACE
		};  /* XXX: helper */

		/* ToInteger() coercion; NaN -> 0, infinities are clamped to 0 and 10 */
		nspace = (duk_small_int_t) duk_to_int_clamped(thr, idx_space, 0 /*minval*/, 10 /*maxval*/);
		DUK_ASSERT(nspace >= 0 && nspace <= 10);

		duk_push_lstring(thr, spaces, (duk_size_t) nspace);
		js_ctx->h_gap = duk_known_hstring(thr, -1);
		DUK_ASSERT(js_ctx->h_gap != NULL);
	} else if (duk_is_string_notsymbol(thr, idx_space)) {
		duk_dup(thr, idx_space);
		duk_substring(thr, -1, 0, 10);  /* clamp to 10 chars */
		js_ctx->h_gap = duk_known_hstring(thr, -1);
	} else {
		/* nop */
	}

	if (js_ctx->h_gap != NULL) {
		/* If gap is empty, behave as if not given at all.  Check
		 * against byte length because character length is more
		 * expensive.
		 */
		if (DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) == 0) {
			js_ctx->h_gap = NULL;
		}
	}

	/* [ ... buf loop (proplist) (gap) ] */

	/*
	 *  Fast path: assume no mutation, iterate object property tables
	 *  directly; bail out if that assumption doesn't hold.
	 */

#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
	if (js_ctx->h_replacer == NULL &&  /* replacer is a mutation risk */
	    js_ctx->idx_proplist == -1) {  /* proplist is very rare */
		duk_int_t pcall_rc;
		duk_small_uint_t prev_ms_base_flags;

		DUK_DD(DUK_DDPRINT("try JSON.stringify() fast path"));

		/* Use recursion_limit to ensure we don't overwrite js_ctx->visiting[]
		 * array so we don't need two counter checks in the fast path.  The
		 * slow path has a much larger recursion limit which we'll use if
		 * necessary.
		 */
		DUK_ASSERT(DUK_USE_JSON_ENC_RECLIMIT >= DUK_JSON_ENC_LOOPARRAY);
		js_ctx->recursion_limit = DUK_JSON_ENC_LOOPARRAY;
		DUK_ASSERT(js_ctx->recursion_depth == 0);

		/* Execute the fast path in a protected call.  If any error is thrown,
		 * fall back to the slow path.  This includes e.g. recursion limit
		 * because the fast path has a smaller recursion limit (and simpler,
		 * limited loop detection).
		 */

		duk_dup(thr, idx_value);

		/* Must prevent finalizers which may have arbitrary side effects. */
		prev_ms_base_flags = thr->heap->ms_base_flags;
		thr->heap->ms_base_flags |=
		        DUK_MS_FLAG_NO_OBJECT_COMPACTION;      /* Avoid attempt to compact any objects. */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	                         1 /*idx_replacer*/,
	                         0 /*flags*/);
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_json_object_stringify(duk_hthread *thr) {
	duk_bi_json_stringify_helper(thr,
	                             0 /*idx_value*/,
	                             1 /*idx_replacer*/,
	                             2 /*idx_space*/,
	                             0 /*flags*/);
	return 1;
}

#endif  /* DUK_USE_JSON_BUILTIN */

#endif  /* DUK_USE_JSON_SUPPORT */

/* automatic undefs */
#undef DUK__EMIT_1
#undef DUK__EMIT_2
#undef DUK__EMIT_CSTR
#undef DUK__EMIT_HSTR
#undef DUK__EMIT_STRIDX
#undef DUK__JSON_DECSTR_BUFSIZE
#undef DUK__JSON_DECSTR_CHUNKSIZE
#undef DUK__JSON_ENCSTR_CHUNKSIZE
#undef DUK__JSON_MAX_ESC_LEN
#undef DUK__JSON_STRINGIFY_BUFSIZE
#undef DUK__MKESC
#undef DUK__UNEMIT_1
#line 1 "duk_bi_math.c"
/*
 *  Math built-ins
 */

/* #include duk_internal.h -> already included */

#if defined(DUK_USE_MATH_BUILTIN)

/*
 *  Use static helpers which can work with math.h functions matching
 *  the following signatures. This is not portable if any of these math
 *  functions is actually a macro.
 *
 *  Typing here is intentionally 'double' wherever values interact with
 *  the standard library APIs.
 */

typedef double (*duk__one_arg_func)(double);
typedef double (*duk__two_arg_func)(double, double);

DUK_LOCAL duk_ret_t duk__math_minmax(duk_hthread *thr, duk_double_t initial, duk__two_arg_func min_max) {
	duk_idx_t n = duk_get_top(thr);
	duk_idx_t i;
	duk_double_t res = initial;
	duk_double_t t;

	/*
	 *  Note: fmax() does not match the E5 semantics.  E5 requires
	 *  that if -any- input to Math.max() is a NaN, the result is a
	 *  NaN.  fmax() will return a NaN only if -both- inputs are NaN.
	 *  Same applies to fmin().
	 *
	 *  Note: every input value must be coerced with ToNumber(), even
	 *  if we know the result will be a NaN anyway: ToNumber() may have
	 *  side effects for which even order of evaluation matters.
	 */

	for (i = 0; i < n; i++) {
		t = duk_to_number(thr, i);
		if (DUK_FPCLASSIFY(t) == DUK_FP_NAN || DUK_FPCLASSIFY(res) == DUK_FP_NAN) {
			/* Note: not normalized, but duk_push_number() will normalize */
			res = (duk_double_t) DUK_DOUBLE_NAN;
		} else {
			res = (duk_double_t) min_max(res, (double) t);
		}
	}

	duk_push_number(thr, res);
	return 1;
}

DUK_LOCAL double duk__fmin_fixed(double x, double y) {
	/* fmin() with args -0 and +0 is not guaranteed to return
	 * -0 as ECMAScript requires.
	 */
	if (x == 0 && y == 0) {
		duk_double_union du1, du2;
		du1.d = x;
		du2.d = y;

		/* Already checked to be zero so these must hold, and allow us
		 * to check for "x is -0 or y is -0" by ORing the high parts
		 * for comparison.
		 */
		DUK_ASSERT(du1.ui[DUK_DBL_IDX_UI0] == 0 || du1.ui[DUK_DBL_IDX_UI0] == 0x80000000UL);
		DUK_ASSERT(du2.ui[DUK_DBL_IDX_UI0] == 0 || du2.ui[DUK_DBL_IDX_UI0] == 0x80000000UL);

		/* XXX: what's the safest way of creating a negative zero? */
		if ((du1.ui[DUK_DBL_IDX_UI0] | du2.ui[DUK_DBL_IDX_UI0]) != 0) {
			/* Enter here if either x or y (or both) is -0. */
			return -0.0;
		} else {
			return +0.0;
		}
	}
	return duk_double_fmin(x, y);
}

DUK_LOCAL double duk__fmax_fixed(double x, double y) {
	/* fmax() with args -0 and +0 is not guaranteed to return
	 * +0 as ECMAScript requires.
	 */
	if (x == 0 && y == 0) {
		if (DUK_SIGNBIT(x) == 0 || DUK_SIGNBIT(y) == 0) {
			return +0.0;
		} else {
			return -0.0;
		}
	}
	return duk_double_fmax(x, y);
}

#if defined(DUK_USE_ES6)
DUK_LOCAL double duk__cbrt(double x) {

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

/* order must match constants in genbuiltins.py */
DUK_LOCAL const duk__two_arg_func duk__two_arg_funcs[] = {
#if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS)
	duk__atan2_fixed,
	duk_js_arith_pow
#else
	duk__atan2_fixed,
	duk_js_arith_pow
#endif
};

DUK_INTERNAL duk_ret_t duk_bi_math_object_onearg_shared(duk_hthread *thr) {
	duk_small_int_t fun_idx = duk_get_current_magic(thr);
	duk__one_arg_func fun;
	duk_double_t arg1;

	DUK_ASSERT(fun_idx >= 0);
	DUK_ASSERT(fun_idx < (duk_small_int_t) (sizeof(duk__one_arg_funcs) / sizeof(duk__one_arg_func)));
	arg1 = duk_to_number(thr, 0);
	fun = duk__one_arg_funcs[fun_idx];
	duk_push_number(thr, (duk_double_t) fun((double) arg1));
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_math_object_twoarg_shared(duk_hthread *thr) {
	duk_small_int_t fun_idx = duk_get_current_magic(thr);
	duk__two_arg_func fun;
	duk_double_t arg1;
	duk_double_t arg2;

	DUK_ASSERT(fun_idx >= 0);
	DUK_ASSERT(fun_idx < (duk_small_int_t) (sizeof(duk__two_arg_funcs) / sizeof(duk__two_arg_func)));
	arg1 = duk_to_number(thr, 0);  /* explicit ordered evaluation to match coercion semantics */
	arg2 = duk_to_number(thr, 1);
	fun = duk__two_arg_funcs[fun_idx];
	duk_push_number(thr, (duk_double_t) fun((double) arg1, (double) arg2));
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_math_object_max(duk_hthread *thr) {
	return duk__math_minmax(thr, -DUK_DOUBLE_INFINITY, duk__fmax_fixed);
}

DUK_INTERNAL duk_ret_t duk_bi_math_object_min(duk_hthread *thr) {
	return duk__math_minmax(thr, DUK_DOUBLE_INFINITY, duk__fmin_fixed);
}

DUK_INTERNAL duk_ret_t duk_bi_math_object_random(duk_hthread *thr) {
	duk_push_number(thr, (duk_double_t) DUK_UTIL_GET_RANDOM_DOUBLE(thr));
	return 1;
}

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_math_object_hypot(duk_hthread *thr) {
	/*
	 *  E6 Section 20.2.2.18: Math.hypot
	 *
	 *  - If no arguments are passed, the result is +0.
	 *  - If any argument is +inf, the result is +inf.
	 *  - If any argument is -inf, the result is +inf.
	 *  - If no argument is +inf or -inf, and any argument is NaN, the result is
	 *    NaN.
	 *  - If all arguments are either +0 or -0, the result is +0.
	 */

	duk_idx_t nargs;
	duk_idx_t i;
	duk_bool_t found_nan;
	duk_double_t max;
	duk_double_t sum, summand;
	duk_double_t comp, prelim;
	duk_double_t t;

	nargs = duk_get_top(thr);

	/* Find the highest value.  Also ToNumber() coerces. */
	max = 0.0;
	found_nan = 0;
	for (i = 0; i < nargs; i++) {
		t = DUK_FABS(duk_to_number(thr, i));
		if (DUK_FPCLASSIFY(t) == DUK_FP_NAN) {
			found_nan = 1;
		} else {
			max = duk_double_fmax(max, t);
		}
	}

	/* Early return cases. */
	if (max == DUK_DOUBLE_INFINITY) {
		duk_push_number(thr, DUK_DOUBLE_INFINITY);
		return 1;
	} else if (found_nan) {
		duk_push_number(thr, DUK_DOUBLE_NAN);
		return 1;
	} else if (max == 0.0) {
		duk_push_number(thr, 0.0);
		/* Otherwise we'd divide by zero. */
		return 1;
	}

	/* Use Kahan summation and normalize to the highest value to minimize
	 * floating point rounding error and avoid overflow.
	 *
	 * https://en.wikipedia.org/wiki/Kahan_summation_algorithm
	 */
	sum = 0.0;
	comp = 0.0;
	for (i = 0; i < nargs; i++) {
		t = DUK_FABS(duk_get_number(thr, i)) / max;
		summand = (t * t) - comp;
		prelim = sum + summand;
		comp = (prelim - sum) - summand;
		sum = prelim;
	}

	duk_push_number(thr, (duk_double_t) DUK_SQRT(sum) * max);
	return 1;
}
#endif  /* DUK_USE_ES6 */

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_math_object_sign(duk_hthread *thr) {
	duk_double_t d;

	d = duk_to_number(thr, 0);
	if (duk_double_is_nan(d)) {
		DUK_ASSERT(duk_is_nan(thr, -1));
		return 1;  /* NaN input -> return NaN */
	}
	if (d == 0.0) {
		/* Zero sign kept, i.e. -0 -> -0, +0 -> +0. */
		return 1;
	}
	duk_push_int(thr, (d > 0.0 ? 1 : -1));
	return 1;
}
#endif  /* DUK_USE_ES6 */

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_math_object_clz32(duk_hthread *thr) {
	duk_uint32_t x;
	duk_small_uint_t i;

#if defined(DUK_USE_PREFER_SIZE)
	duk_uint32_t mask;

	x = duk_to_uint32(thr, 0);
	for (i = 0, mask = 0x80000000UL; mask != 0; mask >>= 1) {
		if (x & mask) {
			break;
		}
		i++;
	}
	DUK_ASSERT(i <= 32);
	duk_push_uint(thr, i);
	return 1;
#else  /* DUK_USE_PREFER_SIZE */
	i = 0;
	x = duk_to_uint32(thr, 0);
	if (x & 0xffff0000UL) {
		x >>= 16;
	} else {
		i += 16;
	}
	if (x & 0x0000ff00UL) {
		x >>= 8;
	} else {
		i += 8;
	}
	if (x & 0x000000f0UL) {
		x >>= 4;
	} else {
		i += 4;
	}
	if (x & 0x0000000cUL) {
		x >>= 2;
	} else {
		i += 2;
	}
	if (x & 0x00000002UL) {
		x >>= 1;
	} else {
		i += 1;
	}
	if (x & 0x00000001UL) {
		;
	} else {
		i += 1;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	duk_uint32_t x, y, z;

	x = duk_to_uint32(thr, 0);
	y = duk_to_uint32(thr, 1);
	z = x * y;

	/* While arguments are ToUint32() coerced and the multiplication
	 * is unsigned as such, the final result is curiously interpreted
	 * as a signed 32-bit value.
	 */
	duk_push_i32(thr, (duk_int32_t) z);
	return 1;
}
#endif  /* DUK_USE_ES6 */

#endif  /* DUK_USE_MATH_BUILTIN */
#line 1 "duk_bi_number.c"
/*
 *  Number built-ins
 */

/* #include duk_internal.h -> already included */

#if defined(DUK_USE_NUMBER_BUILTIN)

DUK_LOCAL duk_double_t duk__push_this_number_plain(duk_hthread *thr) {
	duk_hobject *h;

	/* Number built-in accepts a plain number or a Number object (whose
	 * internal value is operated on).  Other types cause TypeError.
	 */

	duk_push_this(thr);
	if (duk_is_number(thr, -1)) {
		DUK_DDD(DUK_DDDPRINT("plain number value: %!T", (duk_tval *) duk_get_tval(thr, -1)));
		goto done;
	}
	h = duk_get_hobject(thr, -1);
	if (!h ||
	    (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_NUMBER)) {
		DUK_DDD(DUK_DDDPRINT("unacceptable this value: %!T", (duk_tval *) duk_get_tval(thr, -1)));
		DUK_ERROR_TYPE(thr, "number expected");
		DUK_WO_NORETURN(return 0.0;);
	}
	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
	DUK_ASSERT(duk_is_number(thr, -1));
	DUK_DDD(DUK_DDDPRINT("number object: %!T, internal value: %!T",
	                     (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));
	duk_remove_m2(thr);

 done:
	return duk_get_number(thr, -1);
}

DUK_INTERNAL duk_ret_t duk_bi_number_constructor(duk_hthread *thr) {
	duk_idx_t nargs;
	duk_hobject *h_this;

	/*
	 *  The Number constructor uses ToNumber(arg) for number coercion
	 *  (coercing an undefined argument to NaN).  However, if the
	 *  argument is not given at all, +0 must be used instead.  To do
	 *  this, a vararg function is used.
	 */

	nargs = duk_get_top(thr);
	if (nargs == 0) {
		duk_push_int(thr, 0);
	}
	duk_to_number(thr, 0);
	duk_set_top(thr, 1);
	DUK_ASSERT_TOP(thr, 1);

	if (!duk_is_constructor_call(thr)) {
		return 1;
	}

	/*
	 *  E5 Section 15.7.2.1 requires that the constructed object
	 *  must have the original Number.prototype as its internal
	 *  prototype.  However, since Number.prototype is non-writable
	 *  and non-configurable, this doesn't have to be enforced here:
	 *  The default object (bound to 'this') is OK, though we have
	 *  to change its class.
	 *
	 *  Internal value set to ToNumber(arg) or +0; if no arg given,
	 *  ToNumber(undefined) = NaN, so special treatment is needed
	 *  (above).  String internal value is immutable.
	 */

	/* XXX: helper */
	duk_push_this(thr);
	h_this = duk_known_hobject(thr, -1);
	DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_NUMBER);

	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_this) == thr->builtins[DUK_BIDX_NUMBER_PROTOTYPE]);
	DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_this) == DUK_HOBJECT_CLASS_NUMBER);
	DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_this));

	duk_dup_0(thr);  /* -> [ val obj val ] */
	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
	return 0;  /* no return value -> don't replace created value */
}

DUK_INTERNAL duk_ret_t duk_bi_number_prototype_value_of(duk_hthread *thr) {
	(void) duk__push_this_number_plain(thr);
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_string(duk_hthread *thr) {
	duk_small_int_t radix;
	duk_small_uint_t n2s_flags;

	(void) duk__push_this_number_plain(thr);
	if (duk_is_undefined(thr, 0)) {
		radix = 10;
	} else {
		radix = (duk_small_int_t) duk_to_int_check_range(thr, 0, 2, 36);
	}
	DUK_DDD(DUK_DDDPRINT("radix=%ld", (long) radix));

	n2s_flags = 0;

	duk_numconv_stringify(thr,
	                      radix /*radix*/,
	                      0 /*digits*/,
	                      n2s_flags /*flags*/);
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_hthread *thr) {
	/* XXX: just use toString() for now; permitted although not recommended.
	 * nargs==1, so radix is passed to toString().
	 */
	return duk_bi_number_prototype_to_string(thr);
}

/*
 *  toFixed(), toExponential(), toPrecision()
 */

/* XXX: shared helper for toFixed(), toExponential(), toPrecision()? */

DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_fixed(duk_hthread *thr) {
	duk_small_int_t frac_digits;
	duk_double_t d;
	duk_small_int_t c;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	c = (duk_small_int_t) DUK_FPCLASSIFY(d);
	if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
		goto use_to_string;
	}

	frac_digits = (duk_small_int_t) duk_to_int_check_range(thr, 0, 0, 20);

	n2s_flags = DUK_N2S_FLAG_FORCE_EXP |
	           (frac_undefined ? 0 : DUK_N2S_FLAG_FIXED_FORMAT);

	duk_numconv_stringify(thr,
	                      10 /*radix*/,
	                      frac_digits + 1 /*leading digit + fractions*/,
	                      n2s_flags /*flags*/);
	return 1;

 use_to_string:
	DUK_ASSERT_TOP(thr, 2);
	duk_to_string(thr, -1);
	return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_hthread *thr) {
	/* The specification has quite awkward order of coercion and
	 * checks for toPrecision().  The operations below are a bit
	 * reordered, within constraints of observable side effects.
	 */

	duk_double_t d;
	duk_small_int_t prec;
	duk_small_int_t c;
	duk_small_uint_t n2s_flags;

	DUK_ASSERT_TOP(thr, 1);

	d = duk__push_this_number_plain(thr);
	if (duk_is_undefined(thr, 0)) {
		goto use_to_string;
	}
	DUK_ASSERT_TOP(thr, 2);

	duk_to_int(thr, 0);  /* for side effects */

	c = (duk_small_int_t) DUK_FPCLASSIFY(d);
	if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
		goto use_to_string;
	}

	prec = (duk_small_int_t) duk_to_int_check_range(thr, 0, 1, 21);

	n2s_flags = DUK_N2S_FLAG_FIXED_FORMAT |
	            DUK_N2S_FLAG_NO_ZERO_PAD;

	duk_numconv_stringify(thr,
	                      10 /*radix*/,
	                      prec /*digits*/,
	                      n2s_flags /*flags*/);
	return 1;

 use_to_string:
	/* Used when precision is undefined; also used for NaN (-> "NaN"),
	 * and +/- infinity (-> "Infinity", "-Infinity").
	 */

	DUK_ASSERT_TOP(thr, 2);
	duk_to_string(thr, -1);
	return 1;
}

/*
 *  ES2015 isFinite() etc
 */

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_number_check_shared(duk_hthread *thr) {
	duk_int_t magic;
	duk_bool_t ret = 0;

	if (duk_is_number(thr, 0)) {
		duk_double_t d;

		magic = duk_get_current_magic(thr);
		d = duk_get_number(thr, 0);

		switch (magic) {
		case 0:  /* isFinite() */
			ret = duk_double_is_finite(d);
			break;
		case 1:  /* isInteger() */
			ret = duk_double_is_integer(d);
			break;
		case 2:  /* isNaN() */
			ret = duk_double_is_nan(d);
			break;
		default:  /* isSafeInteger() */
			DUK_ASSERT(magic == 3);
			ret = duk_double_is_safe_integer(d);
		}
	}

	duk_push_boolean(thr, ret);
	return 1;
}
#endif  /* DUK_USE_ES6 */

#endif  /* DUK_USE_NUMBER_BUILTIN */
#line 1 "duk_bi_object.c"
/*
 *  Object built-ins
 */

/* #include duk_internal.h -> already included */

/* Needed even when Object built-in disabled. */
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr) {
	duk_tval *tv;

	tv = DUK_HTHREAD_THIS_PTR(thr);
	duk_push_class_string_tval(thr, tv, 0 /*avoid_side_effects*/);
	return 1;
}

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_hthread *thr) {
	duk_uint_t arg_mask;

	arg_mask = duk_get_type_mask(thr, 0);

	if (!duk_is_constructor_call(thr) &&  /* not a constructor call */
	    ((arg_mask & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED)) == 0)) {  /* and argument not null or undefined */
		duk_to_object(thr, 0);
		return 1;
	}

	/* Pointer and buffer primitive values are treated like other
	 * primitives values which have a fully fledged object counterpart:
	 * promote to an object value.  Lightfuncs and plain buffers are
	 * coerced with ToObject() even they could also be returned as is.
	 */
	if (arg_mask & (DUK_TYPE_MASK_OBJECT |
	                DUK_TYPE_MASK_STRING |
	                DUK_TYPE_MASK_BOOLEAN |
	                DUK_TYPE_MASK_NUMBER |
	                DUK_TYPE_MASK_POINTER |
	                DUK_TYPE_MASK_BUFFER |
	                DUK_TYPE_MASK_LIGHTFUNC)) {
		/* For DUK_TYPE_OBJECT the coercion is a no-op and could
		 * be checked for explicitly, but Object(obj) calls are
		 * not very common so opt for minimal footprint.
		 */
		duk_to_object(thr, 0);
		return 1;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	duk_int_t pos;

	/* XXX: faster implementation */

	h = duk_push_this_coercible_to_string(thr);
	DUK_ASSERT(h != NULL);

	pos = duk_to_int(thr, 0);

	if (sizeof(duk_size_t) >= sizeof(duk_uint_t)) {
		/* Cast to duk_size_t works in this case:
		 * - If pos < 0, (duk_size_t) pos will always be
		 *   >= max_charlen, and result will be the empty string
		 *   (see duk_substring()).
		 * - If pos >= 0, pos + 1 cannot wrap.
		 */
		DUK_ASSERT((duk_size_t) DUK_INT_MIN >= DUK_HSTRING_MAX_BYTELEN);
		DUK_ASSERT((duk_size_t) DUK_INT_MAX + 1U > (duk_size_t) DUK_INT_MAX);
		duk_substring(thr, -1, (duk_size_t) pos, (duk_size_t) pos + 1U);
	} else {
		/* If size_t is smaller than int, explicit bounds checks
		 * are needed because an int may wrap multiple times.
		 */
		if (DUK_UNLIKELY(pos < 0 || (duk_uint_t) pos >= (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h))) {
			duk_push_hstring_empty(thr);
		} else {
			duk_substring(thr, -1, (duk_size_t) pos, (duk_size_t) pos + 1U);
		}
	}

	return 1;
}

/* Magic: 0=charCodeAt, 1=codePointAt */
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_hthread *thr) {
	duk_int_t pos;
	duk_hstring *h;
	duk_bool_t clamped;
	duk_uint32_t cp;
	duk_int_t magic;

	/* XXX: faster implementation */

	DUK_DDD(DUK_DDDPRINT("arg=%!T", (duk_tval *) duk_get_tval(thr, 0)));

	h = duk_push_this_coercible_to_string(thr);
	DUK_ASSERT(h != NULL);

	pos = duk_to_int_clamped_raw(thr,
	                             0 /*index*/,
	                             0 /*min(incl)*/,
	                             (duk_int_t) DUK_HSTRING_GET_CHARLEN(h) - 1 /*max(incl)*/,
	                             &clamped /*out_clamped*/);
#if defined(DUK_USE_ES6)
	magic = duk_get_current_magic(thr);
#else
	DUK_ASSERT(duk_get_current_magic(thr) == 0);
	magic = 0;
#endif
	if (clamped) {
		/* For out-of-bounds indices .charCodeAt() returns NaN and
		 * .codePointAt() returns undefined.
		 */
		if (magic != 0) {
			return 0;
		}
		duk_push_nan(thr);
	} else {
		DUK_ASSERT(pos >= 0);
		cp = (duk_uint32_t) duk_hstring_char_code_at_raw(thr, h, (duk_uint_t) pos, (duk_bool_t) magic /*surrogate_aware*/);
		duk_push_u32(thr, cp);
	}
	return 1;
}

/*
 *  substring(), substr(), slice()
 */

/* XXX: any chance of merging these three similar but still slightly
 * different algorithms so that footprint would be reduced?
 */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substring(duk_hthread *thr) {
	duk_hstring *h;
	duk_int_t start_pos, end_pos;
	duk_int_t len;

	h = duk_push_this_coercible_to_string(thr);
	DUK_ASSERT(h != NULL);
	len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);

	/* [ start end str ] */

	start_pos = duk_to_int_clamped(thr, 0, 0, len);
	if (duk_is_undefined(thr, 1)) {
		end_pos = len;
	} else {
		end_pos = duk_to_int_clamped(thr, 1, 0, len);
	}
	DUK_ASSERT(start_pos >= 0 && start_pos <= len);
	DUK_ASSERT(end_pos >= 0 && end_pos <= len);

	if (start_pos > end_pos) {
		duk_int_t tmp = start_pos;
		start_pos = end_pos;
		end_pos = tmp;
	}

	DUK_ASSERT(end_pos >= start_pos);

	duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
	return 1;
}

#if defined(DUK_USE_SECTION_B)
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substr(duk_hthread *thr) {
	duk_hstring *h;
	duk_int_t start_pos, end_pos;
	duk_int_t len;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN


	/* [ start end str ] */

	start_pos = duk_to_int_clamped(thr, 0, -len, len);
	if (start_pos < 0) {
		start_pos = len + start_pos;
	}
	if (duk_is_undefined(thr, 1)) {
		end_pos = len;
	} else {
		end_pos = duk_to_int_clamped(thr, 1, -len, len);
		if (end_pos < 0) {
			end_pos = len + end_pos;
		}
	}
	DUK_ASSERT(start_pos >= 0 && start_pos <= len);
	DUK_ASSERT(end_pos >= 0 && end_pos <= len);

	if (end_pos < start_pos) {
		end_pos = start_pos;
	}

	DUK_ASSERT(end_pos >= start_pos);

	duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
	return 1;
}

/*
 *  Case conversion
 */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_hthread *thr) {
	duk_small_int_t uppercase = duk_get_current_magic(thr);

	(void) duk_push_this_coercible_to_string(thr);
	duk_unicode_case_convert_string(thr, (duk_bool_t) uppercase);
	return 1;
}

/*
 *  indexOf() and lastIndexOf()
 */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_hthread *thr) {
	duk_hstring *h_this;
	duk_hstring *h_search;
	duk_int_t clen_this;
	duk_int_t cpos;
	duk_small_uint_t is_lastindexof = (duk_small_uint_t) duk_get_current_magic(thr);  /* 0=indexOf, 1=lastIndexOf */

	h_this = duk_push_this_coercible_to_string(thr);
	DUK_ASSERT(h_this != NULL);
	clen_this = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h_this);

	h_search = duk_to_hstring(thr, 0);
	DUK_ASSERT(h_search != NULL);

	duk_to_number(thr, 1);
	if (duk_is_nan(thr, 1) && is_lastindexof) {
		/* indexOf: NaN should cause pos to be zero.
		 * lastIndexOf: NaN should cause pos to be +Infinity
		 * (and later be clamped to len).
		 */
		cpos = clen_this;
	} else {
		cpos = duk_to_int_clamped(thr, 1, 0, clen_this);
	}

	cpos = duk__str_search_shared(thr, h_this, h_search, cpos, is_lastindexof /*backwards*/);
	duk_push_int(thr, cpos);
	return 1;
}

/*
 *  replace()
 */

/* XXX: the current implementation works but is quite clunky; it compiles
 * to almost 1,4kB of x86 code so it needs to be simplified (better approach,
 * shared helpers, etc).  Some ideas for refactoring:
 *
 * - a primitive to convert a string into a regexp matcher (reduces matching
 *   code at the cost of making matching much slower)
 * - use replace() as a basic helper for match() and split(), which are both
 *   much simpler
 * - API call to get_prop and to_boolean
 */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_hthread *thr) {
	duk_hstring *h_input;
	duk_hstring *h_match;
	duk_hstring *h_search;
	duk_hobject *h_re;
	duk_bufwriter_ctx bw_alloc;
	duk_bufwriter_ctx *bw;
#if defined(DUK_USE_REGEXP_SUPPORT)
	duk_bool_t is_regexp;
	duk_bool_t is_global;
#endif
	duk_bool_t is_repl_func;
	duk_uint32_t match_start_coff, match_start_boff;
#if defined(DUK_USE_REGEXP_SUPPORT)
	duk_int_t match_caps;
#endif
	duk_uint32_t prev_match_end_boff;
	const duk_uint8_t *r_start, *r_end, *r;   /* repl string scan */
	duk_size_t tmp_sz;

	DUK_ASSERT_TOP(thr, 2);
	h_input = duk_push_this_coercible_to_string(thr);
	DUK_ASSERT(h_input != NULL);

	bw = &bw_alloc;
	DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input));  /* input size is good output starting point */

	DUK_ASSERT_TOP(thr, 4);

	/* stack[0] = search value
	 * stack[1] = replace value
	 * stack[2] = input string
	 * stack[3] = result buffer

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	DUK_D(DUK_DPRINT("allocate heap"));

	/*
	 *  Random config sanity asserts
	 */

	DUK_ASSERT(DUK_USE_STRTAB_MINSIZE >= 64);

	DUK_ASSERT((DUK_HTYPE_STRING & 0x01U) == 0);
	DUK_ASSERT((DUK_HTYPE_BUFFER & 0x01U) == 0);
	DUK_ASSERT((DUK_HTYPE_OBJECT & 0x01U) == 1);  /* DUK_HEAPHDR_IS_OBJECT() relies ont his. */

	/*
	 *  Debug dump type sizes
	 */

#if defined(DUK_USE_DEBUG)
	duk__dump_misc_options();
	duk__dump_type_sizes();
	duk__dump_type_limits();
#endif

	/*
	 *  If selftests enabled, run them as early as possible.
	 */

#if defined(DUK_USE_SELF_TESTS)
	DUK_D(DUK_DPRINT("run self tests"));
	if (duk_selftest_run_tests(alloc_func, realloc_func, free_func, heap_udata) > 0) {
		fatal_func(heap_udata, "self test(s) failed");
	}
	DUK_D(DUK_DPRINT("self tests passed"));
#endif

	/*
	 *  Important assert-like checks that should be enabled even
	 *  when assertions are otherwise not enabled.
	 */

#if defined(DUK_USE_EXEC_REGCONST_OPTIMIZE)
	/* Can't check sizeof() using preprocessor so explicit check.
	 * This will be optimized away in practice; unfortunately a
	 * warning is generated on some compilers as a result.
	 */
#if defined(DUK_USE_PACKED_TVAL)
	if (sizeof(duk_tval) != 8) {
#else
	if (sizeof(duk_tval) != 16) {
#endif
		fatal_func(heap_udata, "sizeof(duk_tval) not 8 or 16, cannot use DUK_USE_EXEC_REGCONST_OPTIMIZE option");
	}
#endif  /* DUK_USE_EXEC_REGCONST_OPTIMIZE */

	/*
	 *  Computed values (e.g. INFINITY)
	 */

#if defined(DUK_USE_COMPUTED_NAN)
	do {
		/* Workaround for some exotic platforms where NAN is missing
		 * and the expression (0.0 / 0.0) does NOT result in a NaN.
		 * Such platforms use the global 'duk_computed_nan' which must
		 * be initialized at runtime.  Use 'volatile' to ensure that
		 * the compiler will actually do the computation and not try
		 * to do constant folding which might result in the original
		 * problem.
		 */
		volatile double dbl1 = 0.0;
		volatile double dbl2 = 0.0;
		duk_computed_nan = dbl1 / dbl2;
	} while (0);
#endif

#if defined(DUK_USE_COMPUTED_INFINITY)
	do {
		/* Similar workaround for INFINITY. */
		volatile double dbl1 = 1.0;
		volatile double dbl2 = 0.0;
		duk_computed_infinity = dbl1 / dbl2;
	} while (0);
#endif

	/*
	 *  Allocate heap struct
	 *
	 *  Use a raw call, all macros expect the heap to be initialized
	 */

#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 1)
	goto failed;
#endif
	DUK_D(DUK_DPRINT("alloc duk_heap object"));
	res = (duk_heap *) alloc_func(heap_udata, sizeof(duk_heap));
	if (!res) {
		goto failed;
	}

	/*
	 *  Zero the struct, and start initializing roughly in order
	 */

	duk_memzero(res, sizeof(*res));
#if defined(DUK_USE_ASSERTIONS)
	res->heap_initializing = 1;
#endif

	/* explicit NULL inits */
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
	res->heap_udata = NULL;
	res->heap_allocated = NULL;
#if defined(DUK_USE_REFERENCE_COUNTING)
	res->refzero_list = NULL;
#endif
#if defined(DUK_USE_FINALIZER_SUPPORT)
	res->finalize_list = NULL;
#if defined(DUK_USE_ASSERTIONS)
	res->currently_finalizing = NULL;
#endif
#endif
#if defined(DUK_USE_CACHE_ACTIVATION)
	res->activation_free = NULL;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	duk_small_uint_t i;

	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		duk_hobject *h;
		h = (duk_hobject *) DUK_LOSE_CONST(duk_rom_builtins_bidx[i]);
		DUK_ASSERT(h != NULL);
		thr->builtins[i] = h;
	}

#if defined(DUK_USE_ROM_GLOBAL_CLONE) || defined(DUK_USE_ROM_GLOBAL_INHERIT)
	/* By default the global object is read-only which is often much
	 * more of an issue than having read-only built-in objects (like
	 * RegExp, Date, etc).  Use a RAM-based copy of the global object
	 * and the global environment object for convenience.
	 */
	duk__duplicate_ram_global_object(thr);
#endif
}
#else  /* DUK_USE_ROM_OBJECTS */
DUK_LOCAL void duk__push_stridx(duk_hthread *thr, duk_bitdecoder_ctx *bd) {
	duk_small_uint_t n;

	n = (duk_small_uint_t) duk_bd_decode_varuint(bd);
	DUK_ASSERT_DISABLE(n >= 0);  /* unsigned */
	DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS);
	duk_push_hstring_stridx(thr, n);
}
DUK_LOCAL void duk__push_string(duk_hthread *thr, duk_bitdecoder_ctx *bd) {
	/* XXX: built-ins data could provide a maximum length that is
	 * actually needed; bitpacked max length is now 256 bytes.
	 */
	duk_uint8_t tmp[DUK_BD_BITPACKED_STRING_MAXLEN];
	duk_small_uint_t len;

	len = duk_bd_decode_bitpacked_string(bd, tmp);
	duk_push_lstring(thr, (const char *) tmp, (duk_size_t) len);
}
DUK_LOCAL void duk__push_stridx_or_string(duk_hthread *thr, duk_bitdecoder_ctx *bd) {
	duk_small_uint_t n;

	n = (duk_small_uint_t) duk_bd_decode_varuint(bd);
	if (n == 0) {
		duk__push_string(thr, bd);
	} else {
		n--;
		DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS);
		duk_push_hstring_stridx(thr, n);
	}
}
DUK_LOCAL void duk__push_double(duk_hthread *thr, duk_bitdecoder_ctx *bd) {
	duk_double_union du;
	duk_small_uint_t i;

	for (i = 0; i < 8; i++) {
		/* Encoding endianness must match target memory layout,
		 * build scripts and genbuiltins.py must ensure this.
		 */
		du.uc[i] = (duk_uint8_t) duk_bd_decode(bd, 8);
	}

	duk_push_number(thr, du.d);  /* push operation normalizes NaNs */
}

DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
	duk_bitdecoder_ctx bd_ctx;
	duk_bitdecoder_ctx *bd = &bd_ctx;  /* convenience */
	duk_hobject *h;
	duk_small_uint_t i, j;

	DUK_D(DUK_DPRINT("INITBUILTINS BEGIN: DUK_NUM_BUILTINS=%d, DUK_NUM_BUILTINS_ALL=%d", (int) DUK_NUM_BUILTINS, (int) DUK_NUM_ALL_BUILTINS));

	duk_memzero(&bd_ctx, sizeof(bd_ctx));
	bd->data = (const duk_uint8_t *) duk_builtins_data;
	bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH;

	/*
	 *  First create all built-in bare objects on the empty valstack.
	 *
	 *  Built-ins in the index range [0,DUK_NUM_BUILTINS-1] have value
	 *  stack indices matching their eventual thr->builtins[] index.
	 *
	 *  Built-ins in the index range [DUK_NUM_BUILTINS,DUK_NUM_ALL_BUILTINS]
	 *  will exist on the value stack during init but won't be placed
	 *  into thr->builtins[].  These are objects referenced in some way
	 *  from thr->builtins[] roots but which don't need to be indexed by
	 *  Duktape through thr->builtins[] (e.g. user custom objects).
	 *
	 *  Internal prototypes will be incorrect (NULL) at this stage.
	 */

	duk_require_stack(thr, DUK_NUM_ALL_BUILTINS);

	DUK_DD(DUK_DDPRINT("create empty built-ins"));
	DUK_ASSERT_TOP(thr, 0);
	for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
		duk_small_uint_t class_num;
		duk_small_int_t len = -1;  /* must be signed */

		class_num = (duk_small_uint_t) duk_bd_decode_varuint(bd);
		len = (duk_small_int_t) duk_bd_decode_flagged_signed(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/);

		if (class_num == DUK_HOBJECT_CLASS_FUNCTION) {
			duk_small_uint_t natidx;
			duk_small_int_t c_nargs;  /* must hold DUK_VARARGS */
			duk_c_function c_func;
			duk_int16_t magic;

			DUK_DDD(DUK_DDDPRINT("len=%ld", (long) len));
			DUK_ASSERT(len >= 0);

			natidx = (duk_small_uint_t) duk_bd_decode_varuint(bd);
			DUK_ASSERT(natidx != 0);
			c_func = duk_bi_native_functions[natidx];
			DUK_ASSERT(c_func != NULL);

			c_nargs = (duk_small_int_t) duk_bd_decode_flagged_signed(bd, DUK__NARGS_BITS, len /*def_value*/);
			if (c_nargs == DUK__NARGS_VARARGS_MARKER) {
				c_nargs = DUK_VARARGS;
			}

			/* XXX: set magic directly here? (it could share the c_nargs arg) */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	duk_ptrdiff_t alloc_end_off;
	duk_ptrdiff_t end_off;
	duk_ptrdiff_t bottom_off;
	duk_ptrdiff_t top_off;

	if (thr->valstack == NULL) {
		DUK_D(DUK_DPRINT("skip valstack torture realloc, valstack is NULL"));
		return;
	}

	alloc_end_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_alloc_end - (duk_uint8_t *) thr->valstack);
	end_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
	bottom_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack);
	top_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack);
	alloc_size = (duk_size_t) alloc_end_off;
	if (alloc_size == 0) {
		DUK_D(DUK_DPRINT("skip valstack torture realloc, alloc_size is zero"));
		return;
	}

	/* Use DUK_ALLOC_RAW() to avoid side effects. */
	new_ptr = (duk_tval *) DUK_ALLOC_RAW(thr->heap, alloc_size);
	if (new_ptr != NULL) {
		duk_memcpy((void *) new_ptr, (const void *) thr->valstack, alloc_size);
		duk_memset((void *) thr->valstack, 0x55, alloc_size);
		DUK_FREE_CHECKED(thr, (void *) thr->valstack);
		thr->valstack = new_ptr;
		thr->valstack_alloc_end = (duk_tval *) ((duk_uint8_t *) new_ptr + alloc_end_off);
		thr->valstack_end = (duk_tval *) ((duk_uint8_t *) new_ptr + end_off);
		thr->valstack_bottom = (duk_tval *) ((duk_uint8_t *) new_ptr + bottom_off);
		thr->valstack_top = (duk_tval *) ((duk_uint8_t *) new_ptr + top_off);
	} else {
		DUK_D(DUK_DPRINT("failed to realloc valstack for torture, ignore"));
	}
}
#endif  /* DUK_USE_FINALIZER_TORTURE */
#line 1 "duk_js_arith.c"
/*
 *  Shared helpers for arithmetic operations
 */

/* #include duk_internal.h -> already included */

/* ECMAScript modulus ('%') does not match IEEE 754 "remainder" operation
 * (implemented by remainder() in C99) but does seem to match ANSI C fmod().
 * Compare E5 Section 11.5.3 and "man fmod".
 */
DUK_INTERNAL double duk_js_arith_mod(double d1, double d2) {
#if defined(DUK_USE_POW_WORKAROUNDS)
	/* Specific fixes to common fmod() implementation issues:
	 * - test-bug-mingw-math-issues.js
	 */
	if (DUK_ISINF(d2)) {
		if (DUK_ISINF(d1)) {
			return DUK_DOUBLE_NAN;
		} else {
			return d1;
		}
	} else if (d1 == 0.0) {
		/* d1 +/-0 is returned as is (preserving sign) except when
		 * d2 is zero or NaN.
		 */
		if (d2 == 0.0 || DUK_ISNAN(d2)) {
			return DUK_DOUBLE_NAN;
		} else {
			return d1;
		}
	}
#else
	/* Some ISO C assumptions. */
	DUK_ASSERT(DUK_FMOD(1.0, DUK_DOUBLE_INFINITY) == 1.0);
	DUK_ASSERT(DUK_FMOD(-1.0, DUK_DOUBLE_INFINITY) == -1.0);
	DUK_ASSERT(DUK_FMOD(1.0, -DUK_DOUBLE_INFINITY) == 1.0);
	DUK_ASSERT(DUK_FMOD(-1.0, -DUK_DOUBLE_INFINITY) == -1.0);
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY)));
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY)));
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY)));
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY)));
	DUK_ASSERT(DUK_FMOD(0.0, 1.0) == 0.0 && DUK_SIGNBIT(DUK_FMOD(0.0, 1.0)) == 0);
	DUK_ASSERT(DUK_FMOD(-0.0, 1.0) == 0.0 && DUK_SIGNBIT(DUK_FMOD(-0.0, 1.0)) != 0);
	DUK_ASSERT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY) == 0.0 && DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0);
	DUK_ASSERT(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY) == 0.0 && DUK_SIGNBIT(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY)) != 0);
	DUK_ASSERT(DUK_FMOD(0.0, -DUK_DOUBLE_INFINITY) == 0.0 && DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0);
	DUK_ASSERT(DUK_FMOD(-0.0, -DUK_DOUBLE_INFINITY) == 0.0 && DUK_SIGNBIT(DUK_FMOD(-0.0, -DUK_DOUBLE_INFINITY)) != 0);
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, 0.0)));
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, 0.0)));
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, -0.0)));
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, -0.0)));
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, DUK_DOUBLE_NAN)));
	DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, DUK_DOUBLE_NAN)));
#endif

	return (duk_double_t) DUK_FMOD((double) d1, (double) d2);
}

/* Shared helper for Math.pow() and exponentiation operator. */
DUK_INTERNAL double duk_js_arith_pow(double x, double y) {
	/* The ANSI C pow() semantics differ from ECMAScript.
	 *
	 * E.g. when x==1 and y is +/- infinite, the ECMAScript required
	 * result is NaN, while at least Linux pow() returns 1.
	 */

	duk_small_int_t cx, cy, sx;

	DUK_UNREF(cx);
	DUK_UNREF(sx);
	cy = (duk_small_int_t) DUK_FPCLASSIFY(y);

	if (cy == DUK_FP_NAN) {
		goto ret_nan;
	}
	if (DUK_FABS(x) == 1.0 && cy == DUK_FP_INFINITE) {
		goto ret_nan;
	}

#if defined(DUK_USE_POW_WORKAROUNDS)
	/* Specific fixes to common pow() implementation issues:
	 *   - test-bug-netbsd-math-pow.js: NetBSD 6.0 on x86 (at least)
	 *   - test-bug-mingw-math-issues.js
	 */
	cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
	if (cx == DUK_FP_ZERO && y < 0.0) {
		sx = (duk_small_int_t) DUK_SIGNBIT(x);
		if (sx == 0) {
			/* Math.pow(+0,y) should be Infinity when y<0.  NetBSD pow()
			 * returns -Infinity instead when y is <0 and finite.  The
			 * if-clause also catches y == -Infinity (which works even
			 * without the fix).
			 */
			return DUK_DOUBLE_INFINITY;
		} else {
			/* Math.pow(-0,y) where y<0 should be:
			 *   - -Infinity if y<0 and an odd integer
			 *   - Infinity if y<0 but not an odd integer
			 * NetBSD pow() returns -Infinity for all finite y<0.  The
			 * if-clause also catches y == -Infinity (which works even
			 * without the fix).
			 */

			/* fmod() return value has same sign as input (negative) so
			 * the result here will be in the range ]-2,0], -1 indicates
			 * odd.  If x is -Infinity, NaN is returned and the odd check
			 * always concludes "not odd" which results in desired outcome.
			 */
			double tmp = DUK_FMOD(y, 2);
			if (tmp == -1.0) {
				return -DUK_DOUBLE_INFINITY;
			} else {
				/* Not odd, or y == -Infinity */
				return DUK_DOUBLE_INFINITY;
			}
		}
	} else if (cx == DUK_FP_NAN) {
		if (y == 0.0) {
			/* NaN ** +/- 0 should always be 1, but is NaN on
			 * at least some Cygwin/MinGW versions.
			 */
			return 1.0;
		}
	}
#else
	/* Some ISO C assumptions. */
	DUK_ASSERT(DUK_POW(DUK_DOUBLE_NAN, 0.0) == 1.0);
	DUK_ASSERT(DUK_ISINF(DUK_POW(0.0, -1.0)) && DUK_SIGNBIT(DUK_POW(0.0, -1.0)) == 0);
	DUK_ASSERT(DUK_ISINF(DUK_POW(-0.0, -2.0)) && DUK_SIGNBIT(DUK_POW(-0.0, -2.0)) == 0);
	DUK_ASSERT(DUK_ISINF(DUK_POW(-0.0, -3.0)) && DUK_SIGNBIT(DUK_POW(-0.0, -3.0)) != 0);
#endif

	return DUK_POW(x, y);

 ret_nan:
	return DUK_DOUBLE_NAN;
}
#line 1 "duk_js_call.c"
/*
 *  Call handling.
 *
 *  duk_handle_call_unprotected():
 *
 *    - Unprotected call to ECMAScript or Duktape/C function, from native
 *      code or bytecode executor.
 *
 *    - Also handles Ecma-to-Ecma calls which reuses a currently running
 *      executor instance to avoid native recursion.  Call setup is done
 *      normally, but just before calling the bytecode executor a special
 *      return code is used to indicate that a calling executor is reused.
 *
 *    - Also handles tailcalls, i.e. reuse of current duk_activation.
 *
 *    - Also handles setup for initial Duktape.Thread.resume().
 *
 *  duk_handle_safe_call():
 *
 *    - Protected C call within current activation.
 *
 *  setjmp() and local variables have a nasty interaction, see execution.rst;
 *  non-volatile locals modified after setjmp() call are not guaranteed to
 *  keep their value and can cause compiler or compiler version specific
 *  difficult to replicate issues.
 *
 *  See 'execution.rst'.
 */

/* #include duk_internal.h -> already included */

/* XXX: heap->error_not_allowed for success path too? */

/*
 *  Limit check helpers.
 */

/* Allow headroom for calls during error augmentation (see GH-191).
 * We allow space for 10 additional recursions, with one extra
 * for, e.g. a print() call at the deepest level, and an extra
 * +1 for protected call wrapping.

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	/*
	 *  Addition operator is different from other arithmetic
	 *  operations in that it also provides string concatenation.
	 *  Hence it is implemented separately.
	 *
	 *  There is a fast path for number addition.  Other cases go
	 *  through potentially multiple coercions as described in the
	 *  E5 specification.  It may be possible to reduce the number
	 *  of coercions, but this must be done carefully to preserve
	 *  the exact semantics.
	 *
	 *  E5 Section 11.6.1.
	 *
	 *  Custom types also have special behavior implemented here.
	 */

	duk_double_union du;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(tv_x != NULL);  /* may be reg or const */
	DUK_ASSERT(tv_y != NULL);  /* may be reg or const */
	DUK_ASSERT_DISABLE(idx_z >= 0);  /* unsigned */
	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr));

	/*
	 *  Fast paths
	 */

#if defined(DUK_USE_FASTINT)
	if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) {
		duk_int64_t v1, v2, v3;
		duk_int32_t v3_hi;
		duk_tval *tv_z;

		/* Input values are signed 48-bit so we can detect overflow
		 * reliably from high bits or just a comparison.
		 */

		v1 = DUK_TVAL_GET_FASTINT(tv_x);
		v2 = DUK_TVAL_GET_FASTINT(tv_y);
		v3 = v1 + v2;
		v3_hi = (duk_int32_t) (v3 >> 32);
		if (DUK_LIKELY(v3_hi >= DUK_I64_CONSTANT(-0x8000) && v3_hi <= DUK_I64_CONSTANT(0x7fff))) {
			tv_z = thr->valstack_bottom + idx_z;
			DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_z, v3);  /* side effects */
			return;
		} else {
			/* overflow, fall through */
			;
		}
	}
#endif  /* DUK_USE_FASTINT */

	if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) {
#if !defined(DUK_USE_EXEC_PREFER_SIZE)
		duk_tval *tv_z;
#endif

		du.d = DUK_TVAL_GET_NUMBER(tv_x) + DUK_TVAL_GET_NUMBER(tv_y);
#if defined(DUK_USE_EXEC_PREFER_SIZE)
		duk_push_number(thr, du.d);  /* will NaN normalize result */
		duk_replace(thr, (duk_idx_t) idx_z);
#else  /* DUK_USE_EXEC_PREFER_SIZE */
		DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
		DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
		tv_z = thr->valstack_bottom + idx_z;
		DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_z, du.d);  /* side effects */
#endif  /* DUK_USE_EXEC_PREFER_SIZE */
		return;
	}

	/*
	 *  Slow path: potentially requires function calls for coercion
	 */

	duk_push_tval(thr, tv_x);
	duk_push_tval(thr, tv_y);
	duk_to_primitive(thr, -2, DUK_HINT_NONE);  /* side effects -> don't use tv_x, tv_y after */
	duk_to_primitive(thr, -1, DUK_HINT_NONE);

	/* Since Duktape 2.x plain buffers are treated like ArrayBuffer. */
	if (duk_is_string(thr, -2) || duk_is_string(thr, -1)) {
		/* Symbols shouldn't technically be handled here, but should
		 * go into the default ToNumber() coercion path instead and
		 * fail there with a TypeError.  However, there's a ToString()
		 * in duk_concat_2() which also fails with TypeError so no
		 * explicit check is needed.
		 */
		duk_concat_2(thr);  /* [... s1 s2] -> [... s1+s2] */
	} else {
		duk_double_t d1, d2;

		d1 = duk_to_number_m2(thr);
		d2 = duk_to_number_m1(thr);
		DUK_ASSERT(duk_is_number(thr, -2));
		DUK_ASSERT(duk_is_number(thr, -1));
		DUK_ASSERT_DOUBLE_IS_NORMALIZED(d1);
		DUK_ASSERT_DOUBLE_IS_NORMALIZED(d2);

		du.d = d1 + d2;
		duk_pop_2_unsafe(thr);
		duk_push_number(thr, du.d);  /* will NaN normalize result */
	}
	duk_replace(thr, (duk_idx_t) idx_z);  /* side effects */
}

DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_uint_fast_t idx_z, duk_small_uint_fast_t opcode) {
	/*
	 *  Arithmetic operations other than '+' have number-only semantics
	 *  and are implemented here.  The separate switch-case here means a
	 *  "double dispatch" of the arithmetic opcode, but saves code space.
	 *
	 *  E5 Sections 11.5, 11.5.1, 11.5.2, 11.5.3, 11.6, 11.6.1, 11.6.2, 11.6.3.
	 */

	duk_double_t d1, d2;
	duk_double_union du;
	duk_small_uint_fast_t opcode_shifted;
#if defined(DUK_USE_FASTINT) || !defined(DUK_USE_EXEC_PREFER_SIZE)
	duk_tval *tv_z;
#endif

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(tv_x != NULL);  /* may be reg or const */
	DUK_ASSERT(tv_y != NULL);  /* may be reg or const */
	DUK_ASSERT_DISABLE(idx_z >= 0);  /* unsigned */
	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr));

	opcode_shifted = opcode >> 2;  /* Get base opcode without reg/const modifiers. */

#if defined(DUK_USE_FASTINT)
	if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) {
		duk_int64_t v1, v2, v3;
		duk_int32_t v3_hi;

		v1 = DUK_TVAL_GET_FASTINT(tv_x);
		v2 = DUK_TVAL_GET_FASTINT(tv_y);

		switch (opcode_shifted) {
		case DUK_OP_SUB >> 2: {
			v3 = v1 - v2;
			break;
		}
		case DUK_OP_MUL >> 2: {
			/* Must ensure result is 64-bit (no overflow); a
			 * simple and sufficient fast path is to allow only
			 * 32-bit inputs.  Avoid zero inputs to avoid
			 * negative zero issues (-1 * 0 = -0, for instance).
			 */
			if (v1 >= DUK_I64_CONSTANT(-0x80000000) && v1 <= DUK_I64_CONSTANT(0x7fffffff) && v1 != 0 &&
			    v2 >= DUK_I64_CONSTANT(-0x80000000) && v2 <= DUK_I64_CONSTANT(0x7fffffff) && v2 != 0) {
				v3 = v1 * v2;
			} else {
				goto skip_fastint;
			}
			break;
		}
		case DUK_OP_DIV >> 2: {
			/* Don't allow a zero divisor.  Fast path check by
			 * "verifying" with multiplication.  Also avoid zero
			 * dividend to avoid negative zero issues (0 / -1 = -0
			 * for instance).

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

		if (DUK_LIKELY(v3_hi >= DUK_I64_CONSTANT(-0x8000) && v3_hi <= DUK_I64_CONSTANT(0x7fff))) {
			tv_z = thr->valstack_bottom + idx_z;
			DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_z, v3);  /* side effects */
			return;
		}
		/* fall through if overflow etc */
	}
 skip_fastint:
#endif  /* DUK_USE_FASTINT */

	if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) {
		/* fast path */
		d1 = DUK_TVAL_GET_NUMBER(tv_x);
		d2 = DUK_TVAL_GET_NUMBER(tv_y);
	} else {
		duk_push_tval(thr, tv_x);
		duk_push_tval(thr, tv_y);
		d1 = duk_to_number_m2(thr);  /* side effects */
		d2 = duk_to_number_m1(thr);
		DUK_ASSERT(duk_is_number(thr, -2));
		DUK_ASSERT(duk_is_number(thr, -1));
		DUK_ASSERT_DOUBLE_IS_NORMALIZED(d1);
		DUK_ASSERT_DOUBLE_IS_NORMALIZED(d2);
		duk_pop_2_unsafe(thr);
	}

	switch (opcode_shifted) {
	case DUK_OP_SUB >> 2: {
		du.d = d1 - d2;
		break;
	}
	case DUK_OP_MUL >> 2: {
		du.d = d1 * d2;
		break;
	}
	case DUK_OP_DIV >> 2: {
		/* Division-by-zero is undefined behavior, so
		 * rely on a helper.
		 */
		du.d = duk_double_div(d1, d2);
		break;
	}
	case DUK_OP_MOD >> 2: {
		du.d = duk__compute_mod(d1, d2);
		break;
	}
#if defined(DUK_USE_ES7_EXP_OPERATOR)
	case DUK_OP_EXP >> 2: {
		du.d = duk__compute_exp(d1, d2);
		break;
	}
#endif
	default: {
		DUK_UNREACHABLE();
		du.d = DUK_DOUBLE_NAN;  /* should not happen */
		break;
	}
	}

#if defined(DUK_USE_EXEC_PREFER_SIZE)
	duk_push_number(thr, du.d);  /* will NaN normalize result */
	duk_replace(thr, (duk_idx_t) idx_z);
#else  /* DUK_USE_EXEC_PREFER_SIZE */
	/* important to use normalized NaN with 8-byte tagged types */
	DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
	DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
	tv_z = thr->valstack_bottom + idx_z;
	DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_z, du.d);  /* side effects */
#endif  /* DUK_USE_EXEC_PREFER_SIZE */
}

DUK_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_binary_op(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_fast_t idx_z, duk_small_uint_fast_t opcode) {
	/*
	 *  Binary bitwise operations use different coercions (ToInt32, ToUint32)
	 *  depending on the operation.  We coerce the arguments first using
	 *  ToInt32(), and then cast to an 32-bit value if necessary.  Note that
	 *  such casts must be correct even if there is no native 32-bit type
	 *  (e.g., duk_int32_t and duk_uint32_t are 64-bit).
	 *
	 *  E5 Sections 11.10, 11.7.1, 11.7.2, 11.7.3
	 */

	duk_int32_t i1, i2, i3;
	duk_uint32_t u1, u2, u3;
#if defined(DUK_USE_FASTINT)
	duk_int64_t fi3;
#else
	duk_double_t d3;
#endif
	duk_small_uint_fast_t opcode_shifted;
#if defined(DUK_USE_FASTINT) || !defined(DUK_USE_EXEC_PREFER_SIZE)
	duk_tval *tv_z;
#endif

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(tv_x != NULL);  /* may be reg or const */
	DUK_ASSERT(tv_y != NULL);  /* may be reg or const */
	DUK_ASSERT_DISABLE(idx_z >= 0);  /* unsigned */
	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr));

	opcode_shifted = opcode >> 2;  /* Get base opcode without reg/const modifiers. */

#if defined(DUK_USE_FASTINT)
	if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) {
		i1 = (duk_int32_t) DUK_TVAL_GET_FASTINT_I32(tv_x);
		i2 = (duk_int32_t) DUK_TVAL_GET_FASTINT_I32(tv_y);
	}
	else
#endif  /* DUK_USE_FASTINT */
	{
		duk_push_tval(thr, tv_x);
		duk_push_tval(thr, tv_y);
		i1 = duk_to_int32(thr, -2);
		i2 = duk_to_int32(thr, -1);
		duk_pop_2_unsafe(thr);
	}

	switch (opcode_shifted) {
	case DUK_OP_BAND >> 2: {
		i3 = i1 & i2;
		break;
	}
	case DUK_OP_BOR >> 2: {
		i3 = i1 | i2;

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	}
	case DUK_OP_BXOR >> 2: {
		i3 = i1 ^ i2;
		break;
	}
	case DUK_OP_BASL >> 2: {
		/* Signed shift, named "arithmetic" (asl) because the result
		 * is signed, e.g. 4294967295 << 1 -> -2.  Note that result
		 * must be masked.
		 */

		u2 = ((duk_uint32_t) i2) & 0xffffffffUL;
		i3 = (duk_int32_t) (((duk_uint32_t) i1) << (u2 & 0x1fUL));  /* E5 Section 11.7.1, steps 7 and 8 */
		i3 = i3 & ((duk_int32_t) 0xffffffffUL);                     /* Note: left shift, should mask */
		break;
	}
	case DUK_OP_BASR >> 2: {
		/* signed shift */

		u2 = ((duk_uint32_t) i2) & 0xffffffffUL;
		i3 = i1 >> (u2 & 0x1fUL);                      /* E5 Section 11.7.2, steps 7 and 8 */
		break;
	}
	case DUK_OP_BLSR >> 2: {
		/* unsigned shift */

		u1 = ((duk_uint32_t) i1) & 0xffffffffUL;
		u2 = ((duk_uint32_t) i2) & 0xffffffffUL;

		/* special result value handling */
		u3 = u1 >> (u2 & 0x1fUL);     /* E5 Section 11.7.2, steps 7 and 8 */
#if defined(DUK_USE_FASTINT)
		fi3 = (duk_int64_t) u3;
		goto fastint_result_set;
#else
		d3 = (duk_double_t) u3;
		goto result_set;
#endif
	}
	default: {
		DUK_UNREACHABLE();
		i3 = 0;  /* should not happen */
		break;
	}
	}

#if defined(DUK_USE_FASTINT)
	/* Result is always fastint compatible. */
	/* XXX: Set 32-bit result (but must then handle signed and
	 * unsigned results separately).
	 */
	fi3 = (duk_int64_t) i3;

 fastint_result_set:
	tv_z = thr->valstack_bottom + idx_z;
	DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_z, fi3);  /* side effects */
#else  /* DUK_USE_FASTINT */
	d3 = (duk_double_t) i3;

 result_set:
	DUK_ASSERT(!DUK_ISNAN(d3));            /* 'd3' is never NaN, so no need to normalize */
	DUK_ASSERT_DOUBLE_IS_NORMALIZED(d3);   /* always normalized */

#if defined(DUK_USE_EXEC_PREFER_SIZE)
	duk_push_number(thr, d3);  /* would NaN normalize result, but unnecessary */
	duk_replace(thr, (duk_idx_t) idx_z);
#else  /* DUK_USE_EXEC_PREFER_SIZE */
	tv_z = thr->valstack_bottom + idx_z;
	DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_z, d3);  /* side effects */
#endif  /* DUK_USE_EXEC_PREFER_SIZE */
#endif  /* DUK_USE_FASTINT */
}

/* In-place unary operation. */
DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_uint_fast_t idx_src, duk_uint_fast_t idx_dst, duk_small_uint_fast_t opcode) {
	/*
	 *  Arithmetic operations other than '+' have number-only semantics
	 *  and are implemented here.  The separate switch-case here means a
	 *  "double dispatch" of the arithmetic opcode, but saves code space.
	 *
	 *  E5 Sections 11.5, 11.5.1, 11.5.2, 11.5.3, 11.6, 11.6.1, 11.6.2, 11.6.3.
	 */

	duk_tval *tv;
	duk_double_t d1;
	duk_double_union du;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(opcode == DUK_OP_UNM || opcode == DUK_OP_UNP);
	DUK_ASSERT_DISABLE(idx_src >= 0);
	DUK_ASSERT_DISABLE(idx_dst >= 0);

	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src);

#if defined(DUK_USE_FASTINT)
	if (DUK_TVAL_IS_FASTINT(tv)) {
		duk_int64_t v1, v2;

		v1 = DUK_TVAL_GET_FASTINT(tv);
		if (opcode == DUK_OP_UNM) {
			/* The smallest fastint is no longer 48-bit when
			 * negated.  Positive zero becames negative zero
			 * (cannot be represented) when negated.
			 */
			if (DUK_LIKELY(v1 != DUK_FASTINT_MIN && v1 != 0)) {
				v2 = -v1;
				tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
				DUK_TVAL_SET_FASTINT_UPDREF(thr, tv, v2);
				return;
			}
		} else {
			/* ToNumber() for a fastint is a no-op. */
			DUK_ASSERT(opcode == DUK_OP_UNP);
			v2 = v1;
			tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
			DUK_TVAL_SET_FASTINT_UPDREF(thr, tv, v2);
			return;
		}
		/* fall through if overflow etc */
	}
#endif  /* DUK_USE_FASTINT */

	if (DUK_TVAL_IS_NUMBER(tv)) {
		d1 = DUK_TVAL_GET_NUMBER(tv);
	} else {
		d1 = duk_to_number_tval(thr, tv);  /* side effects */
	}

	if (opcode == DUK_OP_UNP) {
		/* ToNumber() for a double is a no-op, but unary plus is
		 * used to force a fastint check so do that here.
		 */
		du.d = d1;
		DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
#if defined(DUK_USE_FASTINT)
		tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
		DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF(thr, tv, du.d);  /* always 'fast', i.e. inlined */
		return;
#endif
	} else {
		DUK_ASSERT(opcode == DUK_OP_UNM);
		du.d = -d1;
		DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);  /* mandatory if du.d is a NaN */
		DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
	}

	/* XXX: size optimize: push+replace? */
	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
	DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, du.d);
}

DUK_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_not(duk_hthread *thr, duk_uint_fast_t idx_src, duk_uint_fast_t idx_dst) {
	/*
	 *  E5 Section 11.4.8
	 */

	duk_tval *tv;
	duk_int32_t i1, i2;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT_DISABLE(idx_src >= 0);
	DUK_ASSERT_DISABLE(idx_dst >= 0);
	DUK_ASSERT((duk_uint_t) idx_src < (duk_uint_t) duk_get_top(thr));
	DUK_ASSERT((duk_uint_t) idx_dst < (duk_uint_t) duk_get_top(thr));

	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src);

#if defined(DUK_USE_FASTINT)
	if (DUK_TVAL_IS_FASTINT(tv)) {
		i1 = (duk_int32_t) DUK_TVAL_GET_FASTINT_I32(tv);
	}
	else
#endif  /* DUK_USE_FASTINT */
	{
		duk_push_tval(thr, tv);
		i1 = duk_to_int32(thr, -1);  /* side effects */
		duk_pop_unsafe(thr);
	}

	/* Result is always fastint compatible. */
	i2 = ~i1;
	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
	DUK_TVAL_SET_I32_UPDREF(thr, tv, i2);  /* side effects */
}

DUK_LOCAL DUK__INLINE_PERF void duk__vm_logical_not(duk_hthread *thr, duk_uint_fast_t idx_src, duk_uint_fast_t idx_dst) {
	/*
	 *  E5 Section 11.4.9
	 */

	duk_tval *tv;
	duk_bool_t res;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT_DISABLE(idx_src >= 0);
	DUK_ASSERT_DISABLE(idx_dst >= 0);
	DUK_ASSERT((duk_uint_t) idx_src < (duk_uint_t) duk_get_top(thr));
	DUK_ASSERT((duk_uint_t) idx_dst < (duk_uint_t) duk_get_top(thr));

	/* ToBoolean() does not require any operations with side effects so
	 * we can do it efficiently.  For footprint it would be better to use
	 * duk_js_toboolean() and then push+replace to the result slot.
	 */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

		return 1;
	}
#if defined(DUK_USE_FASTINT)
	case DUK_TAG_FASTINT:
		if (DUK_TVAL_GET_FASTINT(tv) != 0) {
			return 1;
		} else {
			return 0;
		}
#endif
	default: {
		/* number */
		duk_double_t d;
#if defined(DUK_USE_PREFER_SIZE)
		int c;
#endif
		DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
		DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
		d = DUK_TVAL_GET_DOUBLE(tv);
#if defined(DUK_USE_PREFER_SIZE)
		c = DUK_FPCLASSIFY((double) d);
		if (c == DUK_FP_ZERO || c == DUK_FP_NAN) {
			return 0;
		} else {
			return 1;
		}
#else
		DUK_ASSERT(duk_double_is_nan_or_zero(d) == 0 || duk_double_is_nan_or_zero(d) == 1);
		return duk_double_is_nan_or_zero(d) ^ 1;
#endif
	}
	}
	DUK_UNREACHABLE();
}

/*
 *  ToNumber()  (E5 Section 9.3)
 *
 *  Value to convert must be on stack top, and is popped before exit.
 *
 *  See: http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
 *       http://www.cs.indiana.edu/~burger/fp/index.html
 *
 *  Notes on the conversion:
 *
 *    - There are specific requirements on the accuracy of the conversion
 *      through a "Mathematical Value" (MV), so this conversion is not
 *      trivial.
 *
 *    - Quick rejects (e.g. based on first char) are difficult because
 *      the grammar allows leading and trailing white space.
 *
 *    - Quick reject based on string length is difficult even after
 *      accounting for white space; there may be arbitrarily many
 *      decimal digits.
 *
 *    - Standard grammar allows decimal values ("123"), hex values
 *      ("0x123") and infinities
 *
 *    - Unlike source code literals, ToNumber() coerces empty strings
 *      and strings with only whitespace to zero (not NaN).
 */

/* E5 Section 9.3.1 */
DUK_LOCAL duk_double_t duk__tonumber_string_raw(duk_hthread *thr) {
	duk_small_uint_t s2n_flags;
	duk_double_t d;

	DUK_ASSERT(duk_is_string(thr, -1));

	/* Quite lenient, e.g. allow empty as zero, but don't allow trailing
	 * garbage.
	 */
	s2n_flags = DUK_S2N_FLAG_TRIM_WHITE |
	            DUK_S2N_FLAG_ALLOW_EXP |
	            DUK_S2N_FLAG_ALLOW_PLUS |
	            DUK_S2N_FLAG_ALLOW_MINUS |
	            DUK_S2N_FLAG_ALLOW_INF |
	            DUK_S2N_FLAG_ALLOW_FRAC |
	            DUK_S2N_FLAG_ALLOW_NAKED_FRAC |
	            DUK_S2N_FLAG_ALLOW_EMPTY_FRAC |
	            DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO |
	            DUK_S2N_FLAG_ALLOW_LEADING_ZERO |
	            DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT |
	            DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT |
	            DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT;

	duk_numconv_parse(thr, 10 /*radix*/, s2n_flags);

#if defined(DUK_USE_PREFER_SIZE)
	d = duk_get_number(thr, -1);
	duk_pop_unsafe(thr);
#else
	thr->valstack_top--;
	DUK_ASSERT(DUK_TVAL_IS_NUMBER(thr->valstack_top));
	DUK_ASSERT(DUK_TVAL_IS_DOUBLE(thr->valstack_top));  /* no fastint conversion in numconv now */
	DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(thr->valstack_top));
	d = DUK_TVAL_GET_DOUBLE(thr->valstack_top);  /* assumes not a fastint */
	DUK_TVAL_SET_UNDEFINED(thr->valstack_top);
#endif

	return d;
}

DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) {
	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(tv != NULL);

	switch (DUK_TVAL_GET_TAG(tv)) {
	case DUK_TAG_UNDEFINED: {
		/* return a specific NaN (although not strictly necessary) */
		duk_double_union du;
		DUK_DBLUNION_SET_NAN(&du);
		DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
		return du.d;
	}
	case DUK_TAG_NULL: {
		/* +0.0 */
		return 0.0;
	}
	case DUK_TAG_BOOLEAN: {
		if (DUK_TVAL_IS_BOOLEAN_TRUE(tv)) {
			return 1.0;
		}
		return 0.0;
	}
	case DUK_TAG_STRING: {
		/* For Symbols ToNumber() is always a TypeError. */
		duk_hstring *h = DUK_TVAL_GET_STRING(tv);
		if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
			DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL);
			DUK_WO_NORETURN(return 0.0;);
		}
		duk_push_hstring(thr, h);
		return duk__tonumber_string_raw(thr);
	}
	case DUK_TAG_BUFFER:  /* plain buffer treated like object */
	case DUK_TAG_OBJECT: {
		duk_double_t d;
		duk_push_tval(thr, tv);
		duk_to_primitive(thr, -1, DUK_HINT_NUMBER);  /* 'tv' becomes invalid */

		/* recursive call for a primitive value (guaranteed not to cause second
		 * recursion).
		 */
		DUK_ASSERT(duk_get_tval(thr, -1) != NULL);
		d = duk_js_tonumber(thr, duk_get_tval(thr, -1));

		duk_pop_unsafe(thr);
		return d;
	}
	case DUK_TAG_POINTER: {
		/* Coerce like boolean */
		void *p = DUK_TVAL_GET_POINTER(tv);
		return (p != NULL ? 1.0 : 0.0);
	}
	case DUK_TAG_LIGHTFUNC: {
		/* +(function(){}) -> NaN */
		return DUK_DOUBLE_NAN;
	}
#if defined(DUK_USE_FASTINT)
	case DUK_TAG_FASTINT:
		return (duk_double_t) DUK_TVAL_GET_FASTINT(tv);
#endif
	default: {
		/* number */
		DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
		DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
		return DUK_TVAL_GET_DOUBLE(tv);
	}
	}

	DUK_UNREACHABLE();
}

/*
 *  ToInteger()  (E5 Section 9.4)
 */

/* exposed, used by e.g. duk_bi_date.c */
DUK_INTERNAL duk_double_t duk_js_tointeger_number(duk_double_t x) {
#if defined(DUK_USE_PREFER_SIZE)
	duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x);

	if (DUK_UNLIKELY(c == DUK_FP_NAN)) {
		return 0.0;
	} else if (DUK_UNLIKELY(c == DUK_FP_INFINITE)) {
		return x;
	} else {
		/* Finite, including neg/pos zero.  Neg zero sign must be
		 * preserved.
		 */
		return duk_double_trunc_towards_zero(x);
	}
#else  /* DUK_USE_PREFER_SIZE */
	/* NaN and Infinity have the same exponent so it's a cheap
	 * initial check for the rare path.
	 */
	if (DUK_UNLIKELY(duk_double_is_nan_or_inf(x) != 0U)) {
		if (duk_double_is_nan(x)) {
			return 0.0;
		} else {
			return x;
		}
	} else {
		return duk_double_trunc_towards_zero(x);
	}
#endif  /* DUK_USE_PREFER_SIZE */
}

DUK_INTERNAL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv) {
	/* XXX: fastint */
	duk_double_t d = duk_js_tonumber(thr, tv);  /* invalidates tv */
	return duk_js_tointeger_number(d);
}

/*
 *  ToInt32(), ToUint32(), ToUint16()  (E5 Sections 9.5, 9.6, 9.7)
 */

/* combined algorithm matching E5 Sections 9.5 and 9.6 */
DUK_LOCAL duk_double_t duk__toint32_touint32_helper(duk_double_t x, duk_bool_t is_toint32) {
#if defined (DUK_USE_PREFER_SIZE)
	duk_small_int_t c;
#endif

#if defined (DUK_USE_PREFER_SIZE)
	c = (duk_small_int_t) DUK_FPCLASSIFY(x);
	if (c == DUK_FP_NAN || c == DUK_FP_ZERO || c == DUK_FP_INFINITE) {
		return 0.0;
	}
#else
	if (duk_double_is_nan_zero_inf(x)) {
		return 0.0;
	}
#endif

	/* x = sign(x) * floor(abs(x)), i.e. truncate towards zero, keep sign */
	x = duk_double_trunc_towards_zero(x);

	/* NOTE: fmod(x) result sign is same as sign of x, which
	 * differs from what Javascript wants (see Section 9.6).
	 */

	x = DUK_FMOD(x, DUK_DOUBLE_2TO32);    /* -> x in ]-2**32, 2**32[ */

	if (x < 0.0) {
		x += DUK_DOUBLE_2TO32;
	}
	DUK_ASSERT(x >= 0 && x < DUK_DOUBLE_2TO32);  /* -> x in [0, 2**32[ */

	if (is_toint32) {
		if (x >= DUK_DOUBLE_2TO31) {
			/* x in [2**31, 2**32[ */

			x -= DUK_DOUBLE_2TO32;  /* -> x in [-2**31,2**31[ */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN


	d = duk_js_tonumber(thr, tv);  /* invalidates tv */
	d = duk__toint32_touint32_helper(d, 0);
	DUK_ASSERT(DUK_FPCLASSIFY(d) == DUK_FP_ZERO || DUK_FPCLASSIFY(d) == DUK_FP_NORMAL);
	DUK_ASSERT(d >= 0.0 && d <= 4294967295.0);  /* [0x00000000, 0xffffffff] */
	DUK_ASSERT(d == ((duk_double_t) ((duk_uint32_t) d)));  /* whole, won't clip */
	return (duk_uint32_t) d;

}

DUK_INTERNAL duk_uint16_t duk_js_touint16(duk_hthread *thr, duk_tval *tv) {
	/* should be a safe way to compute this */
	return (duk_uint16_t) (duk_js_touint32(thr, tv) & 0x0000ffffU);
}

/*
 *  ToString()  (E5 Section 9.8)
 *  ToObject()  (E5 Section 9.9)
 *  CheckObjectCoercible()  (E5 Section 9.10)
 *  IsCallable()  (E5 Section 9.11)
 *
 *  ==> implemented in the API.
 */

/*
 *  Loose equality, strict equality, and SameValue (E5 Sections 11.9.1, 11.9.4,
 *  9.12).  These have much in common so they can share some helpers.
 *
 *  Future work notes:
 *
 *    - Current implementation (and spec definition) has recursion; this should
 *      be fixed if possible.
 *
 *    - String-to-number coercion should be possible without going through the
 *      value stack (and be more compact) if a shared helper is invoked.
 */

/* Note that this is the same operation for strict and loose equality:
 *  - E5 Section 11.9.3, step 1.c (loose)
 *  - E5 Section 11.9.6, step 4 (strict)
 */

DUK_LOCAL duk_bool_t duk__js_equals_number(duk_double_t x, duk_double_t y) {
#if defined(DUK_USE_PARANOID_MATH)
	/* Straightforward algorithm, makes fewer compiler assumptions. */
	duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
	duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y);
	if (cx == DUK_FP_NAN || cy == DUK_FP_NAN) {
		return 0;
	}
	if (cx == DUK_FP_ZERO && cy == DUK_FP_ZERO) {
		return 1;
	}
	if (x == y) {
		return 1;
	}
	return 0;
#else  /* DUK_USE_PARANOID_MATH */
	/* Better equivalent algorithm.  If the compiler is compliant, C and
	 * ECMAScript semantics are identical for this particular comparison.
	 * In particular, NaNs must never compare equal and zeroes must compare
	 * equal regardless of sign.  Could also use a macro, but this inlines
	 * already nicely (no difference on gcc, for instance).
	 */
	if (x == y) {
		/* IEEE requires that NaNs compare false */
		DUK_ASSERT(DUK_FPCLASSIFY(x) != DUK_FP_NAN);
		DUK_ASSERT(DUK_FPCLASSIFY(y) != DUK_FP_NAN);
		return 1;
	} else {
		/* IEEE requires that zeros compare the same regardless
		 * of their signed, so if both x and y are zeroes, they
		 * are caught above.
		 */
		DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO));
		return 0;
	}
#endif  /* DUK_USE_PARANOID_MATH */
}

DUK_LOCAL duk_bool_t duk__js_samevalue_number(duk_double_t x, duk_double_t y) {
#if defined(DUK_USE_PARANOID_MATH)
	duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
	duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y);

	if (cx == DUK_FP_NAN && cy == DUK_FP_NAN) {
		/* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */
		return 1;
	}
	if (cx == DUK_FP_ZERO && cy == DUK_FP_ZERO) {
		/* Note: cannot assume that a non-zero return value of signbit() would
		 * always be the same -- hence cannot (portably) use something like:
		 *
		 *     signbit(x) == signbit(y)
		 */
		duk_small_int_t sx = DUK_SIGNBIT(x) ? 1 : 0;
		duk_small_int_t sy = DUK_SIGNBIT(y) ? 1 : 0;
		return (sx == sy);
	}

	/* normal comparison; known:
	 *   - both x and y are not NaNs (but one of them can be)
	 *   - both x and y are not zero (but one of them can be)
	 *   - x and y may be denormal or infinite
	 */

	return (x == y);
#else  /* DUK_USE_PARANOID_MATH */
	duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
	duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y);

	if (x == y) {
		/* IEEE requires that NaNs compare false */
		DUK_ASSERT(DUK_FPCLASSIFY(x) != DUK_FP_NAN);
		DUK_ASSERT(DUK_FPCLASSIFY(y) != DUK_FP_NAN);

		/* Using classification has smaller footprint than direct comparison. */
		if (DUK_UNLIKELY(cx == DUK_FP_ZERO && cy == DUK_FP_ZERO)) {
			/* Note: cannot assume that a non-zero return value of signbit() would
			 * always be the same -- hence cannot (portably) use something like:
			 *
			 *     signbit(x) == signbit(y)
			 */
			return duk_double_same_sign(x, y);
		}
		return 1;
	} else {
		/* IEEE requires that zeros compare the same regardless
		 * of their sign, so if both x and y are zeroes, they
		 * are caught above.
		 */
		DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO));

		/* Difference to non-strict/strict comparison is that NaNs compare
		 * equal and signed zero signs matter.
		 */
		if (DUK_UNLIKELY(cx == DUK_FP_NAN && cy == DUK_FP_NAN)) {
			/* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */
			return 1;
		}
		return 0;
	}
#endif  /* DUK_USE_PARANOID_MATH */
}

DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags) {
	duk_uint_t type_mask_x;
	duk_uint_t type_mask_y;

	/* If flags != 0 (strict or SameValue), thr can be NULL.  For loose
	 * equals comparison it must be != NULL.
	 */
	DUK_ASSERT(flags != 0 || thr != NULL);

	/*
	 *  Same type?
	 *
	 *  Note: since number values have no explicit tag in the 8-byte
	 *  representation, need the awkward if + switch.
	 */

#if defined(DUK_USE_FASTINT)
	if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) {
		if (DUK_TVAL_GET_FASTINT(tv_x) == DUK_TVAL_GET_FASTINT(tv_y)) {
			return 1;
		} else {
			return 0;
		}
	}
	else
#endif
	if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) {
		duk_double_t d1, d2;

		/* Catches both doubles and cases where only one argument is
		 * a fastint so can't assume a double.
		 */
		d1 = DUK_TVAL_GET_NUMBER(tv_x);
		d2 = DUK_TVAL_GET_NUMBER(tv_y);
		if (DUK_UNLIKELY((flags & DUK_EQUALS_FLAG_SAMEVALUE) != 0)) {
			/* SameValue */
			return duk__js_samevalue_number(d1, d2);
		} else {
			/* equals and strict equals */
			return duk__js_equals_number(d1, d2);
		}
	} else if (DUK_TVAL_GET_TAG(tv_x) == DUK_TVAL_GET_TAG(tv_y)) {
		switch (DUK_TVAL_GET_TAG(tv_x)) {
		case DUK_TAG_UNDEFINED:
		case DUK_TAG_NULL: {
			return 1;
		}
		case DUK_TAG_BOOLEAN: {
			return DUK_TVAL_GET_BOOLEAN(tv_x) == DUK_TVAL_GET_BOOLEAN(tv_y);
		}
		case DUK_TAG_POINTER: {
			return DUK_TVAL_GET_POINTER(tv_x) == DUK_TVAL_GET_POINTER(tv_y);
		}

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

		return retval;
	}
}
#endif

#if defined(DUK_USE_PARANOID_MATH)
DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk_double_t d2) {
	duk_small_int_t c1, s1, c2, s2;

	DUK_ASSERT(retval == 0 || retval == 1);
	c1 = (duk_small_int_t) DUK_FPCLASSIFY(d1);
	s1 = (duk_small_int_t) DUK_SIGNBIT(d1);
	c2 = (duk_small_int_t) DUK_FPCLASSIFY(d2);
	s2 = (duk_small_int_t) DUK_SIGNBIT(d2);

	if (c1 == DUK_FP_NAN || c2 == DUK_FP_NAN) {
		return 0;  /* Always false, regardless of negation. */
	}

	if (c1 == DUK_FP_ZERO && c2 == DUK_FP_ZERO) {
		/* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0,
		 * steps e, f, and g.
		 */
		return retval;  /* false */
	}

	if (d1 == d2) {
		return retval;  /* false */
	}

	if (c1 == DUK_FP_INFINITE && s1 == 0) {
		/* x == +Infinity */
		return retval;  /* false */
	}

	if (c2 == DUK_FP_INFINITE && s2 == 0) {
		/* y == +Infinity */
		return retval ^ 1;  /* true */
	}

	if (c2 == DUK_FP_INFINITE && s2 != 0) {
		/* y == -Infinity */
		return retval;  /* false */
	}

	if (c1 == DUK_FP_INFINITE && s1 != 0) {
		/* x == -Infinity */
		return retval ^ 1;  /* true */
	}

	if (d1 < d2) {
		return retval ^ 1;  /* true */
	}

	return retval;  /* false */
}
#else  /* DUK_USE_PARANOID_MATH */
DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk_double_t d2) {
	/* This comparison tree relies doesn't match the exact steps in
	 * E5 Section 11.8.5 but should produce the same results.  The
	 * steps rely on exact IEEE semantics for NaNs, etc.
	 */

	DUK_ASSERT(retval == 0 || retval == 1);
	if (d1 < d2) {
		/* In no case should both (d1 < d2) and (d2 < d1) be true.
		 * It's possible that neither is true though, and that's
		 * handled below.
		 */
		DUK_ASSERT(!(d2 < d1));

		/* - d1 < d2, both d1/d2 are normals (not Infinity, not NaN)
		 * - d2 is +Infinity, d1 != +Infinity and NaN
		 * - d1 is -Infinity, d2 != -Infinity and NaN
		 */
		return retval ^ 1;
	} else {
		if (d2 < d1) {
			/* - !(d1 < d2), both d1/d2 are normals (not Infinity, not NaN)
			 * - d1 is +Infinity, d2 != +Infinity and NaN
			 * - d2 is -Infinity, d1 != -Infinity and NaN
			 */
			return retval;
		} else {
			/* - d1 and/or d2 is NaN
			 * - d1 and d2 are both +/- 0
			 * - d1 == d2 (including infinities)
			 */
			if (duk_double_is_nan(d1) || duk_double_is_nan(d2)) {
				/* Note: undefined from Section 11.8.5 always
				 * results in false return (see e.g. Section
				 * 11.8.3) - hence special treatment here.
				 */
				return 0;  /* zero regardless of negation */
			} else {
				return retval;
			}
		}
	}
}
#endif  /* DUK_USE_PARANOID_MATH */

DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags) {
	duk_double_t d1, d2;
	duk_small_int_t rc;
	duk_bool_t retval;

	DUK_ASSERT(DUK_COMPARE_FLAG_NEGATE == 1);  /* Rely on this flag being lowest. */
	retval = flags & DUK_COMPARE_FLAG_NEGATE;
	DUK_ASSERT(retval == 0 || retval == 1);

	/* Fast path for fastints */
#if defined(DUK_USE_FASTINT)
	if (DUK_LIKELY(DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y))) {
		return duk__compare_fastint(retval,
		                            DUK_TVAL_GET_FASTINT(tv_x),
		                            DUK_TVAL_GET_FASTINT(tv_y));
	}
#endif  /* DUK_USE_FASTINT */

	/* Fast path for numbers (one of which may be a fastint) */
#if !defined(DUK_USE_PREFER_SIZE)
	if (DUK_LIKELY(DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y))) {
		return duk__compare_number(retval,
		                           DUK_TVAL_GET_NUMBER(tv_x),
		                           DUK_TVAL_GET_NUMBER(tv_y));
	}
#endif

	/* Slow path */

	duk_push_tval(thr, tv_x);
	duk_push_tval(thr, tv_y);

	if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) {
		duk_to_primitive(thr, -2, DUK_HINT_NUMBER);
		duk_to_primitive(thr, -1, DUK_HINT_NUMBER);
	} else {
		duk_to_primitive(thr, -1, DUK_HINT_NUMBER);
		duk_to_primitive(thr, -2, DUK_HINT_NUMBER);
	}

	/* Note: reuse variables */
	tv_x = DUK_GET_TVAL_NEGIDX(thr, -2);
	tv_y = DUK_GET_TVAL_NEGIDX(thr, -1);

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN


	t = 0;
	for (i = 0; i < 52; i++) {
		bitidx = bitstart + 52 - 1 - i;
		if (bitidx >= nc_ctx->count) {
			v = 0;
		} else if (bitidx < 0) {
			v = 0;
		} else {
			v = nc_ctx->digits[bitidx];
		}
		DUK_ASSERT(v == 0 || v == 1);
		t += v << (i % 32);
		if (i == 31) {
			/* low 32 bits is complete */
			DUK_DBLUNION_SET_LOW32(&u, t);
			t = 0;
		}
	}
	/* t has high mantissa */

	DUK_DDD(DUK_DDDPRINT("mantissa is complete: %08lx %08lx",
	                     (unsigned long) t,
	                     (unsigned long) DUK_DBLUNION_GET_LOW32(&u)));

	DUK_ASSERT(expt >= 0 && expt <= 0x7ffL);
	t += ((duk_uint32_t) expt) << 20;
#if 0  /* caller handles sign change */
	if (negative) {
		t |= 0x80000000U;
	}
#endif
	DUK_DBLUNION_SET_HIGH32(&u, t);

	DUK_DDD(DUK_DDDPRINT("number is complete: %08lx %08lx",
	                     (unsigned long) DUK_DBLUNION_GET_HIGH32(&u),
	                     (unsigned long) DUK_DBLUNION_GET_LOW32(&u)));

	*x = DUK_DBLUNION_GET_DOUBLE(&u);
}

/*
 *  Exposed number-to-string API
 *
 *  Input: [ number ]
 *  Output: [ string ]
 */

DUK_INTERNAL void duk_numconv_stringify(duk_hthread *thr, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags) {
	duk_double_t x;
	duk_small_int_t c;
	duk_small_int_t neg;
	duk_uint32_t uval;
	duk__numconv_stringify_ctx nc_ctx_alloc;  /* large context; around 2kB now */
	duk__numconv_stringify_ctx *nc_ctx = &nc_ctx_alloc;

	x = (duk_double_t) duk_require_number(thr, -1);
	duk_pop(thr);

	/*
	 *  Handle special cases (NaN, infinity, zero).
	 */

	c = (duk_small_int_t) DUK_FPCLASSIFY(x);
	if (DUK_SIGNBIT((double) x)) {
		x = -x;
		neg = 1;
	} else {
		neg = 0;
	}

	/* NaN sign bit is platform specific with unpacked, un-normalized NaNs */
	DUK_ASSERT(c == DUK_FP_NAN || DUK_SIGNBIT((double) x) == 0);

	if (c == DUK_FP_NAN) {
		duk_push_hstring_stridx(thr, DUK_STRIDX_NAN);
		return;
	} else if (c == DUK_FP_INFINITE) {
		if (neg) {
			/* -Infinity */
			duk_push_hstring_stridx(thr, DUK_STRIDX_MINUS_INFINITY);
		} else {
			/* Infinity */
			duk_push_hstring_stridx(thr, DUK_STRIDX_INFINITY);
		}
		return;
	} else if (c == DUK_FP_ZERO) {
		/* We can't shortcut zero here if it goes through special formatting
		 * (such as forced exponential notation).
		 */
		;
	}

	/*
	 *  Handle integers in 32-bit range (that is, [-(2**32-1),2**32-1])
	 *  specially, as they're very likely for embedded programs.  This
	 *  is now done for all radix values.  We must be careful not to use
	 *  the fast path when special formatting (e.g. forced exponential)
	 *  is in force.
	 *
	 *  XXX: could save space by supporting radix 10 only and using
	 *  sprintf "%lu" for the fast path and for exponent formatting.
	 */

	uval = duk_double_to_uint32_t(x);
	if (((double) uval) == x &&  /* integer number in range */
	    flags == 0) {            /* no special formatting */
		/* use bigint area as a temp */
		duk_uint8_t *buf = (duk_uint8_t *) (&nc_ctx->f);
		duk_uint8_t *p = buf;

		DUK_ASSERT(DUK__NUMCONV_CTX_BIGINTS_SIZE >= 32 + 1);  /* max size: radix=2 + sign */
		if (neg && uval != 0) {
			/* no negative sign for zero */
			*p++ = (duk_uint8_t) '-';
		}
		p += duk__dragon4_format_uint32(p, uval, radix);
		duk_push_lstring(thr, (const char *) buf, (duk_size_t) (p - buf));
		return;
	}

	/*
	 *  Dragon4 setup.
	 *
	 *  Convert double from IEEE representation for conversion;
	 *  normal finite values have an implicit leading 1-bit.  The
	 *  slow path algorithm doesn't handle zero, so zero is special
	 *  cased here but still creates a valid nc_ctx, and goes
	 *  through normal formatting in case special formatting has
	 *  been requested (e.g. forced exponential format: 0 -> "0e+0").
	 */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	/*
	 *  Dragon4 slow path digit generation.
	 */

	duk__dragon4_prepare(nc_ctx);  /* setup many variables in nc_ctx */

	DUK_DDD(DUK_DDDPRINT("after prepare:"));
	DUK__BI_PRINT("r", &nc_ctx->r);
	DUK__BI_PRINT("s", &nc_ctx->s);
	DUK__BI_PRINT("mp", &nc_ctx->mp);
	DUK__BI_PRINT("mm", &nc_ctx->mm);

	duk__dragon4_scale(nc_ctx);

	DUK_DDD(DUK_DDDPRINT("after scale; k=%ld", (long) nc_ctx->k));
	DUK__BI_PRINT("r", &nc_ctx->r);
	DUK__BI_PRINT("s", &nc_ctx->s);
	DUK__BI_PRINT("mp", &nc_ctx->mp);
	DUK__BI_PRINT("mm", &nc_ctx->mm);

	duk__dragon4_generate(nc_ctx);

	/*
	 *  Convert and push final string.
	 */

 zero_skip:

	if (flags & DUK_N2S_FLAG_FIXED_FORMAT) {
		/* Perform fixed-format rounding. */
		duk_small_int_t roundpos;
		if (flags & DUK_N2S_FLAG_FRACTION_DIGITS) {
			/* 'roundpos' is relative to nc_ctx->k and increases to the right
			 * (opposite of how 'k' changes).
			 */
			roundpos = -digits;  /* absolute position for digit considered for rounding */
			roundpos = nc_ctx->k - roundpos;
		} else {
			roundpos = digits;
		}
		DUK_DDD(DUK_DDDPRINT("rounding: k=%ld, count=%ld, digits=%ld, roundpos=%ld",
		                     (long) nc_ctx->k, (long) nc_ctx->count, (long) digits, (long) roundpos));
		(void) duk__dragon4_fixed_format_round(nc_ctx, roundpos);

		/* Note: 'count' is currently not adjusted by rounding (i.e. the
		 * digits are not "chopped off".  That shouldn't matter because
		 * the digit position (absolute or relative) is passed on to the
		 * convert-and-push function.
		 */
	}

	duk__dragon4_convert_and_push(nc_ctx, thr, radix, digits, flags, neg);
}

/*
 *  Exposed string-to-number API
 *
 *  Input: [ string ]
 *  Output: [ number ]
 *
 *  If number parsing fails, a NaN is pushed as the result.  If number parsing
 *  fails due to an internal error, an InternalError is thrown.
 */

DUK_INTERNAL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags) {
	duk__numconv_stringify_ctx nc_ctx_alloc;  /* large context; around 2kB now */
	duk__numconv_stringify_ctx *nc_ctx = &nc_ctx_alloc;
	duk_double_t res;
	duk_hstring *h_str;
	duk_int_t expt;
	duk_bool_t expt_neg;
	duk_small_int_t expt_adj;
	duk_small_int_t neg;
	duk_small_int_t dig;
	duk_small_int_t dig_whole;
	duk_small_int_t dig_lzero;
	duk_small_int_t dig_frac;
	duk_small_int_t dig_expt;
	duk_small_int_t dig_prec;
	const duk__exp_limits *explim;
	const duk_uint8_t *p;
	duk_small_int_t ch;

	DUK_DDD(DUK_DDDPRINT("parse number: %!T, radix=%ld, flags=0x%08lx",
	                     (duk_tval *) duk_get_tval(thr, -1),
	                     (long) radix, (unsigned long) flags));

	DUK_ASSERT(radix >= 2 && radix <= 36);
	DUK_ASSERT(radix - 2 < (duk_small_int_t) sizeof(duk__str2num_digits_for_radix));

	/*
	 *  Preliminaries: trim, sign, Infinity check
	 *
	 *  We rely on the interned string having a NUL terminator, which will
	 *  cause a parse failure wherever it is encountered.  As a result, we
	 *  don't need separate pointer checks.
	 *
	 *  There is no special parsing for 'NaN' in the specification although
	 *  'Infinity' (with an optional sign) is allowed in some contexts.
	 *  Some contexts allow plus/minus sign, while others only allow the
	 *  minus sign (like JSON.parse()).
	 *
	 *  Automatic hex number detection (leading '0x' or '0X') and octal
	 *  number detection (leading '0' followed by at least one octal digit)
	 *  is done here too.
	 *
	 *  Symbols are not explicitly rejected here (that's up to the caller).
	 *  If a symbol were passed here, it should ultimately safely fail
	 *  parsing due to a syntax error.
	 */

	if (flags & DUK_S2N_FLAG_TRIM_WHITE) {
		/* Leading / trailing whitespace is sometimes accepted and
		 * sometimes not.  After white space trimming, all valid input
		 * characters are pure ASCII.
		 */
		duk_trim(thr, -1);
	}
	h_str = duk_require_hstring(thr, -1);
	DUK_ASSERT(h_str != NULL);
	p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_str);

	neg = 0;
	ch = *p;
	if (ch == (duk_small_int_t) '+') {
		if ((flags & DUK_S2N_FLAG_ALLOW_PLUS) == 0) {
			DUK_DDD(DUK_DDDPRINT("parse failed: leading plus sign not allowed"));
			goto parse_fail;
		}
		p++;
	} else if (ch == (duk_small_int_t) '-') {
		if ((flags & DUK_S2N_FLAG_ALLOW_MINUS) == 0) {
			DUK_DDD(DUK_DDDPRINT("parse failed: leading minus sign not allowed"));
			goto parse_fail;
		}
		p++;
		neg = 1;
	}

	if ((flags & DUK_S2N_FLAG_ALLOW_INF) && DUK_STRNCMP((const char *) p, "Infinity", 8) == 0) {
		/* Don't check for Infinity unless the context allows it.
		 * 'Infinity' is a valid integer literal in e.g. base-36:
		 *
		 *   parseInt('Infinity', 36)
		 *   1461559270678
		 */

		if ((flags & DUK_S2N_FLAG_ALLOW_GARBAGE) == 0 && p[8] != DUK_ASC_NUL) {
			DUK_DDD(DUK_DDDPRINT("parse failed: trailing garbage after matching 'Infinity' not allowed"));
			goto parse_fail;
		} else {
			res = DUK_DOUBLE_INFINITY;
			goto negcheck_and_ret;
		}
	}
	ch = *p;
	if (ch == (duk_small_int_t) '0') {
		duk_small_int_t detect_radix = 0;
		ch = DUK_LOWERCASE_CHAR_ASCII(p[1]);  /* 'x' or 'X' -> 'x' */
		if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT) && ch == DUK_ASC_LC_X) {
			DUK_DDD(DUK_DDDPRINT("detected 0x/0X hex prefix, changing radix and preventing fractions and exponent"));
			detect_radix = 16;
#if 0
		} else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_LEGACY_OCT_INT) &&
		           (ch >= (duk_small_int_t) '0' && ch <= (duk_small_int_t) '9')) {
			DUK_DDD(DUK_DDDPRINT("detected 0n oct prefix, changing radix and preventing fractions and exponent"));
			detect_radix = 8;

			/* NOTE: if this legacy octal case is added back, it has
			 * different flags and 'p' advance so this needs to be
			 * reworked.
			 */
			flags |= DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO;  /* interpret e.g. '09' as '0', not NaN */
			p += 1;
#endif
		} else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT) && ch == DUK_ASC_LC_O) {
			DUK_DDD(DUK_DDDPRINT("detected 0o oct prefix, changing radix and preventing fractions and exponent"));
			detect_radix = 8;
		} else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT) && ch == DUK_ASC_LC_B) {
			DUK_DDD(DUK_DDDPRINT("detected 0b bin prefix, changing radix and preventing fractions and exponent"));
			detect_radix = 2;
		}
		if (detect_radix > 0) {
			radix = detect_radix;
			/* Clear empty as zero flag: interpret e.g. '0x' and '0xg' as a NaN (= parse error) */
			flags &= ~(DUK_S2N_FLAG_ALLOW_EXP | DUK_S2N_FLAG_ALLOW_EMPTY_FRAC |
			           DUK_S2N_FLAG_ALLOW_FRAC | DUK_S2N_FLAG_ALLOW_NAKED_FRAC |
			           DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO);
			flags |= DUK_S2N_FLAG_ALLOW_LEADING_ZERO;  /* allow e.g. '0x0009' and '0b00010001' */
			p += 2;
		}
	}

	/*
	 *  Scan number and setup for Dragon4.
	 *
	 *  The fast path case is detected during setup: an integer which
	 *  can be converted without rounding, no net exponent.  The fast
	 *  path could be implemented as a separate scan, but may not really
	 *  be worth it: the multiplications for building 'f' are not
	 *  expensive when 'f' is small.
	 *
	 *  The significand ('f') must contain enough bits of (apparent)
	 *  accuracy, so that Dragon4 will generate enough binary output digits.
	 *  For decimal numbers, this means generating a 20-digit significand,
	 *  which should yield enough practical accuracy to parse IEEE doubles.
	 *  In fact, the ECMAScript specification explicitly allows an
	 *  implementation to treat digits beyond 20 as zeroes (and even
	 *  to round the 20th digit upwards).  For non-decimal numbers, the
	 *  appropriate number of digits has been precomputed for comparable
	 *  accuracy.
	 *
	 *  Digit counts:
	 *
	 *    [ dig_lzero ]
	 *      |
	 *     .+-..---[ dig_prec ]----.
	 *     |  ||                   |
	 *     0000123.456789012345678901234567890e+123456
	 *     |     | |                         |  |    |
	 *     `--+--' `------[ dig_frac ]-------'  `-+--'
	 *        |                                   |
	 *    [ dig_whole ]                       [ dig_expt ]
	 *
	 *    dig_frac and dig_expt are -1 if not present
	 *    dig_lzero is only computed for whole number part
	 *
	 *  Parsing state
	 *
	 *     Parsing whole part      dig_frac < 0 AND dig_expt < 0
	 *     Parsing fraction part   dig_frac >= 0 AND dig_expt < 0
	 *     Parsing exponent part   dig_expt >= 0   (dig_frac may be < 0 or >= 0)
	 *
	 *  Note: in case we hit an implementation limit (like exponent range),
	 *  we should throw an error, NOT return NaN or Infinity.  Even with
	 *  very large exponent (or significand) values the final result may be
	 *  finite, so NaN/Infinity would be incorrect.
	 */

	duk__bi_set_small(&nc_ctx->f, 0);
	dig_prec = 0;
	dig_lzero = 0;
	dig_whole = 0;
	dig_frac = -1;
	dig_expt = -1;
	expt = 0;
	expt_adj = 0;  /* essentially tracks digit position of lowest 'f' digit */
	expt_neg = 0;
	for (;;) {
		ch = *p++;

		DUK_DDD(DUK_DDDPRINT("parse digits: p=%p, ch='%c' (%ld), expt=%ld, expt_adj=%ld, "
		                     "dig_whole=%ld, dig_frac=%ld, dig_expt=%ld, dig_lzero=%ld, dig_prec=%ld",
		                     (const void *) p, (int) ((ch >= 0x20 && ch <= 0x7e) ? ch : '?'), (long) ch,
		                     (long) expt, (long) expt_adj, (long) dig_whole, (long) dig_frac,
		                     (long) dig_expt, (long) dig_lzero, (long) dig_prec));
		DUK__BI_PRINT("f", &nc_ctx->f);

		/* Most common cases first. */
		if (ch >= (duk_small_int_t) '0' && ch <= (duk_small_int_t) '9') {
			dig = (duk_small_int_t) ch - '0' + 0;
		} else if (ch == (duk_small_int_t) '.') {
			/* A leading digit is not required in some cases, e.g. accept ".123".
			 * In other cases (JSON.parse()) a leading digit is required.  This
			 * is checked for after the loop.
			 */
			if (dig_frac >= 0 || dig_expt >= 0) {
				if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) {
					DUK_DDD(DUK_DDDPRINT("garbage termination (invalid period)"));
					break;
				} else {
					DUK_DDD(DUK_DDDPRINT("parse failed: period not allowed"));
					goto parse_fail;
				}
			}

			if ((flags & DUK_S2N_FLAG_ALLOW_FRAC) == 0) {
				/* Some contexts don't allow fractions at all; this can't be a
				 * post-check because the state ('f' and expt) would be incorrect.
				 */
				if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) {
					DUK_DDD(DUK_DDDPRINT("garbage termination (invalid first period)"));
					break;
				} else {
					DUK_DDD(DUK_DDDPRINT("parse failed: fraction part not allowed"));
				}
			}

			DUK_DDD(DUK_DDDPRINT("start fraction part"));
			dig_frac = 0;
			continue;
		} else if (ch == (duk_small_int_t) 0) {
			DUK_DDD(DUK_DDDPRINT("NUL termination"));
			break;
		} else if ((flags & DUK_S2N_FLAG_ALLOW_EXP) &&
		           dig_expt < 0 && (ch == (duk_small_int_t) 'e' || ch == (duk_small_int_t) 'E')) {
			/* Note: we don't parse back exponent notation for anything else

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	re_ctx.bytecode = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_bytecode);
	re_ctx.bytecode_end = re_ctx.bytecode + DUK_HSTRING_GET_BYTELEN(h_bytecode);
	re_ctx.saved = NULL;
	re_ctx.recursion_limit = DUK_USE_REGEXP_EXECUTOR_RECLIMIT;
	re_ctx.steps_limit = DUK_RE_EXECUTE_STEPS_LIMIT;

	/* read header */
	pc = re_ctx.bytecode;
	re_ctx.re_flags = duk__bc_get_u32(&re_ctx, &pc);
	re_ctx.nsaved = duk__bc_get_u32(&re_ctx, &pc);
	re_ctx.bytecode = pc;

	DUK_ASSERT(DUK_RE_FLAG_GLOBAL < 0x10000UL);  /* must fit into duk_small_int_t */
	global = (duk_small_int_t) (force_global | (duk_small_int_t) (re_ctx.re_flags & DUK_RE_FLAG_GLOBAL));

	DUK_ASSERT(re_ctx.nsaved >= 2);
	DUK_ASSERT((re_ctx.nsaved % 2) == 0);

	p_buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, sizeof(duk_uint8_t *) * re_ctx.nsaved);  /* rely on zeroing */
	DUK_UNREF(p_buf);
	re_ctx.saved = (const duk_uint8_t **) duk_get_buffer(thr, -1, NULL);
	DUK_ASSERT(re_ctx.saved != NULL);

	/* [ ... re_obj input bc saved_buf ] */

#if defined(DUK_USE_EXPLICIT_NULL_INIT)
	for (i = 0; i < re_ctx.nsaved; i++) {
		re_ctx.saved[i] = (duk_uint8_t *) NULL;
	}
#elif defined(DUK_USE_ZERO_BUFFER_DATA)
	/* buffer is automatically zeroed */
#else
	duk_memzero((void *) p_buf, sizeof(duk_uint8_t *) * re_ctx.nsaved);
#endif

	DUK_DDD(DUK_DDDPRINT("regexp ctx initialized, flags=0x%08lx, nsaved=%ld, recursion_limit=%ld, steps_limit=%ld",
	                     (unsigned long) re_ctx.re_flags, (long) re_ctx.nsaved, (long) re_ctx.recursion_limit,
	                     (long) re_ctx.steps_limit));

	/*
	 *  Get starting character offset for match, and initialize 'sp' based on it.
	 *
	 *  Note: lastIndex is non-configurable so it must be present (we check the
	 *  internal class of the object above, so we know it is).  User code can set
	 *  its value to an arbitrary (garbage) value though; E5 requires that lastIndex
	 *  be coerced to a number before using.  The code below works even if the
	 *  property is missing: the value will then be coerced to zero.
	 *
	 *  Note: lastIndex may be outside Uint32 range even after ToInteger() coercion.
	 *  For instance, ToInteger(+Infinity) = +Infinity.  We track the match offset
	 *  as an integer, but pre-check it to be inside the 32-bit range before the loop.
	 *  If not, the check in E5 Section 15.10.6.2, step 9.a applies.
	 */

	/* XXX: lastIndex handling produces a lot of asm */

	/* [ ... re_obj input bc saved_buf ] */

	duk_get_prop_stridx_short(thr, -4, DUK_STRIDX_LAST_INDEX);  /* -> [ ... re_obj input bc saved_buf lastIndex ] */
	(void) duk_to_int(thr, -1);  /* ToInteger(lastIndex) */
	d = duk_get_number(thr, -1);  /* integer, but may be +/- Infinite, +/- zero (not NaN, though) */
	duk_pop_nodecref_unsafe(thr);

	if (global) {
		if (d < 0.0 || d > (double) DUK_HSTRING_GET_CHARLEN(h_input)) {
			/* match fail */
			char_offset = 0;   /* not really necessary */
			DUK_ASSERT(match == 0);
			goto match_over;
		}
		char_offset = (duk_uint32_t) d;
	} else {
		/* lastIndex must be ignored for non-global regexps, but get the
		 * value for (theoretical) side effects.  No side effects can
		 * really occur, because lastIndex is a normal property and is
		 * always non-configurable for RegExp instances.
		 */
		char_offset = (duk_uint32_t) 0;
	}

	DUK_ASSERT(char_offset <= DUK_HSTRING_GET_CHARLEN(h_input));
	sp = re_ctx.input + duk_heap_strcache_offset_char2byte(thr, h_input, char_offset);

	/*
	 *  Match loop.
	 *
	 *  Try matching at different offsets until match found or input exhausted.
	 */

	/* [ ... re_obj input bc saved_buf ] */

	DUK_ASSERT(match == 0);

	for (;;) {
		/* char offset in [0, h_input->clen] (both ends inclusive), checked before entry */
		DUK_ASSERT_DISABLE(char_offset >= 0);
		DUK_ASSERT(char_offset <= DUK_HSTRING_GET_CHARLEN(h_input));

		/* Note: re_ctx.steps is intentionally not reset, it applies to the entire unanchored match */
		DUK_ASSERT(re_ctx.recursion_depth == 0);

		DUK_DDD(DUK_DDDPRINT("attempt match at char offset %ld; %p [%p,%p]",
		                     (long) char_offset, (const void *) sp,
		                     (const void *) re_ctx.input, (const void *) re_ctx.input_end));

		/*
		 *  Note:
		 *
		 *    - duk__match_regexp() is required not to longjmp() in ordinary "non-match"
		 *      conditions; a longjmp() will terminate the entire matching process.
		 *
		 *    - Clearing saved[] is not necessary because backtracking does it
		 *
		 *    - Backtracking also rewinds re_ctx.recursion back to zero, unless an
		 *      internal/limit error occurs (which causes a longjmp())
		 *
		 *    - If we supported anchored matches, we would break out here
		 *      unconditionally; however, ECMAScript regexps don't have anchored
		 *      matches.  It might make sense to implement a fast bail-out if
		 *      the regexp begins with '^' and sp is not 0: currently we'll just
		 *      run through the entire input string, trivially failing the match

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN

	if (x32 != (duk_uint32_t) 0xefbeaddeUL) {
		DUK__FAILED("DUK_BSWAP32");
	}

	/* >>> struct.unpack('>d', '4000112233445566'.decode('hex'))
	 * (2.008366013071895,)
	 */

	du.uc[0] = 0x40; du.uc[1] = 0x00; du.uc[2] = 0x11; du.uc[3] = 0x22;
	du.uc[4] = 0x33; du.uc[5] = 0x44; du.uc[6] = 0x55; du.uc[7] = 0x66;
	DUK_DBLUNION_DOUBLE_NTOH(&du);
	du_diff = du.d - 2.008366013071895;
#if 0
	DUK_D(DUK_DPRINT("du_diff: %lg\n", (double) du_diff));
#endif
	if (du_diff > 1e-15) {
		/* Allow very small lenience because some compilers won't parse
		 * exact IEEE double constants (happened in matrix testing with
		 * Linux gcc-4.8 -m32 at least).
		 */
#if 0
		DUK_D(DUK_DPRINT("Result of DUK_DBLUNION_DOUBLE_NTOH: %02x %02x %02x %02x %02x %02x %02x %02x\n",
		            (unsigned int) du.uc[0], (unsigned int) du.uc[1],
		            (unsigned int) du.uc[2], (unsigned int) du.uc[3],
		            (unsigned int) du.uc[4], (unsigned int) du.uc[5],
		            (unsigned int) du.uc[6], (unsigned int) du.uc[7]));
#endif
		DUK__FAILED("DUK_DBLUNION_DOUBLE_NTOH");
	}

	return error_count;
}

/*
 *  Basic double / byte union memory layout.
 */

DUK_LOCAL duk_uint_t duk__selftest_double_union_size(void) {
	duk_uint_t error_count = 0;

	if (sizeof(duk__test_double_union) != 8) {
		DUK__FAILED("invalid union size");
	}

	return error_count;
}

/*
 *  Union aliasing, see misc/clang_aliasing.c.
 */

DUK_LOCAL duk_uint_t duk__selftest_double_aliasing(void) {
	/* This testcase fails when Emscripten-generated code runs on Firefox.
	 * It's not an issue because the failure should only affect packed
	 * duk_tval representation, which is not used with Emscripten.
	 */
#if defined(DUK_USE_PACKED_TVAL)
	duk_uint_t error_count = 0;
	duk__test_double_union a, b;

	/* Test signaling NaN and alias assignment in all endianness combinations.
	 */

	/* little endian */
	a.x[0] = 0x11; a.x[1] = 0x22; a.x[2] = 0x33; a.x[3] = 0x44;
	a.x[4] = 0x00; a.x[5] = 0x00; a.x[6] = 0xf1; a.x[7] = 0xff;
	b = a;
	DUK__DBLUNION_CMP_TRUE(&a, &b);

	/* big endian */
	a.x[0] = 0xff; a.x[1] = 0xf1; a.x[2] = 0x00; a.x[3] = 0x00;
	a.x[4] = 0x44; a.x[5] = 0x33; a.x[6] = 0x22; a.x[7] = 0x11;
	b = a;
	DUK__DBLUNION_CMP_TRUE(&a, &b);

	/* mixed endian */
	a.x[0] = 0x00; a.x[1] = 0x00; a.x[2] = 0xf1; a.x[3] = 0xff;
	a.x[4] = 0x11; a.x[5] = 0x22; a.x[6] = 0x33; a.x[7] = 0x44;
	b = a;
	DUK__DBLUNION_CMP_TRUE(&a, &b);

	return error_count;
#else
	DUK_D(DUK_DPRINT("skip double aliasing self test when duk_tval is not packed"));
	return 0;
#endif
}

/*
 *  Zero sign, see misc/tcc_zerosign2.c.
 */

DUK_LOCAL duk_uint_t duk__selftest_double_zero_sign(void) {
	duk_uint_t error_count = 0;
	duk__test_double_union a, b;

	a.d = 0.0;
	b.d = -a.d;
	DUK__DBLUNION_CMP_FALSE(&a, &b);

	return error_count;
}

/*
 *  Rounding mode: Duktape assumes round-to-nearest, check that this is true.
 *  If we had C99 fenv.h we could check that fegetround() == FE_TONEAREST,
 *  but we don't want to rely on that header; and even if we did, it's good
 *  to ensure the rounding actually works.
 */

DUK_LOCAL duk_uint_t duk__selftest_double_rounding(void) {
	duk_uint_t error_count = 0;
	duk__test_double_union a, b, c;

#if 0
	/* Include <fenv.h> and test manually; these trigger failures: */
	fesetround(FE_UPWARD);
	fesetround(FE_DOWNWARD);
	fesetround(FE_TOWARDZERO);

	/* This is the default and passes. */

lib/JavaScript/Duktape/C/lib/duktape.c  view on Meta::CPAN


	return du.d;
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u16_be(duk_uint8_t **p, duk_uint16_t val) {
	union {
		duk_uint8_t b[2];
		duk_uint16_t x;
	} u;

	u.x = DUK_HTON16(val);
	duk_memcpy((void *) (*p), (const void *) u.b, (size_t) 2);
	*p += 2;
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u32_be(duk_uint8_t **p, duk_uint32_t val) {
	union {
		duk_uint8_t b[4];
		duk_uint32_t x;
	} u;

	u.x = DUK_HTON32(val);
	duk_memcpy((void *) (*p), (const void *) u.b, (size_t) 4);
	*p += 4;
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_double_be(duk_uint8_t **p, duk_double_t val) {
	duk_double_union du;
	union {
		duk_uint8_t b[4];
		duk_uint32_t x;
	} u;

	du.d = val;
	u.x = du.ui[DUK_DBL_IDX_UI0];
	u.x = DUK_HTON32(u.x);
	duk_memcpy((void *) (*p), (const void *) u.b, (size_t) 4);
	u.x = du.ui[DUK_DBL_IDX_UI1];
	u.x = DUK_HTON32(u.x);
	duk_memcpy((void *) (*p + 4), (const void *) u.b, (size_t) 4);
	*p += 8;
}
#line 1 "duk_util_cast.c"
/*
 *  Cast helpers.
 *
 *  C99+ coercion is challenging portability-wise because out-of-range casts
 *  may invoke implementation defined or even undefined behavior.  See e.g.
 *  http://blog.frama-c.com/index.php?post/2013/10/09/Overflow-float-integer.
 *
 *  Provide explicit cast helpers which try to avoid implementation defined
 *  or undefined behavior.  These helpers can then be simplified in the vast
 *  majority of cases where the implementation defined or undefined behavior
 *  is not problematic.
 */

/* #include duk_internal.h -> already included */

/* Portable double-to-integer cast which avoids undefined behavior and avoids
 * relying on fmin(), fmax(), or other intrinsics.  Out-of-range results are
 * not assumed by caller, but here value is clamped, NaN converts to minval.
 */
#define DUK__DOUBLE_INT_CAST1(tname,minval,maxval)  do { \
		if (DUK_LIKELY(x >= (duk_double_t) (minval))) { \
			DUK_ASSERT(!DUK_ISNAN(x)); \
			if (DUK_LIKELY(x <= (duk_double_t) (maxval))) { \
				return (tname) x; \
			} else { \
				return (tname) (maxval); \
			} \
		} else { \
			/* NaN or below minval.  Since we don't care about the result \
			 * for out-of-range values, just return the minimum value for \
			 * both. \
			 */ \
			return (tname) (minval); \
		} \
	} while (0)

/* Rely on specific NaN behavior for duk_double_{fmin,fmax}(): if either
 * argument is a NaN, return the second argument.  This avoids a
 * NaN-to-integer cast which is undefined behavior.
 */
#define DUK__DOUBLE_INT_CAST2(tname,minval,maxval)  do { \
		return (tname) duk_double_fmin(duk_double_fmax(x, (duk_double_t) (minval)), (duk_double_t) (maxval)); \
	} while (0)

/* Another solution which doesn't need C99+ behavior for fmin() and fmax(). */
#define DUK__DOUBLE_INT_CAST3(tname,minval,maxval)  do { \
		if (DUK_ISNAN(x)) { \
			/* 0 or any other value is fine. */ \
			return (tname) 0; \
		} else \
			return (tname) DUK_FMIN(DUK_FMAX(x, (duk_double_t) (minval)), (duk_double_t) (maxval)); \
		} \
	} while (0)

/* C99+ solution: relies on specific fmin() and fmax() behavior in C99: if
 * one argument is NaN but the other isn't, the non-NaN argument is returned.
 * Because the limits are non-NaN values, explicit NaN check is not needed.
 * This may not work on all legacy platforms, and also doesn't seem to inline
 * the fmin() and fmax() calls (unless one uses -ffast-math which we don't
 * support).
 */
#define DUK__DOUBLE_INT_CAST4(tname,minval,maxval)  do { \
		return (tname) DUK_FMIN(DUK_FMAX(x, (duk_double_t) (minval)), (duk_double_t) (maxval)); \
	} while (0)

DUK_INTERNAL duk_int_t duk_double_to_int_t(duk_double_t x) {
#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
	/* Real world solution: almost any practical platform will provide
	 * an integer value without any guarantees what it is (which is fine).
	 */
	return (duk_int_t) x;
#else
	DUK__DOUBLE_INT_CAST1(duk_int_t, DUK_INT_MIN, DUK_INT_MAX);
#endif
}

DUK_INTERNAL duk_uint_t duk_double_to_uint_t(duk_double_t x) {
#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
	return (duk_uint_t) x;
#else
	DUK__DOUBLE_INT_CAST1(duk_uint_t, DUK_UINT_MIN, DUK_UINT_MAX);
#endif
}

DUK_INTERNAL duk_int32_t duk_double_to_int32_t(duk_double_t x) {
#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
	return (duk_int32_t) x;
#else
	DUK__DOUBLE_INT_CAST1(duk_int32_t, DUK_INT32_MIN, DUK_INT32_MAX);
#endif
}

DUK_INTERNAL duk_uint32_t duk_double_to_uint32_t(duk_double_t x) {
#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
	return (duk_uint32_t) x;
#else
	DUK__DOUBLE_INT_CAST1(duk_uint32_t, DUK_UINT32_MIN, DUK_UINT32_MAX);
#endif
}

/* Largest IEEE double that doesn't round to infinity in the default rounding
 * mode.  The exact midpoint between (1 - 2^(-24)) * 2^128 and 2^128 rounds to
 * infinity, at least on x64.  This number is one double unit below that
 * midpoint.  See misc/float_cast.c.
 */
#define DUK__FLOAT_ROUND_LIMIT      340282356779733623858607532500980858880.0

/* Maximum IEEE float.  Double-to-float conversion above this would be out of
 * range and thus technically undefined behavior.
 */
#define DUK__FLOAT_MAX              340282346638528859811704183484516925440.0

DUK_INTERNAL duk_float_t duk_double_to_float_t(duk_double_t x) {
	/* Even a double-to-float cast is technically undefined behavior if
	 * the double is out-of-range.  C99 Section 6.3.1.5:
	 *
	 *   If the value being converted is in the range of values that can
	 *   be represented but cannot be represented exactly, the result is
	 *   either the nearest higher or nearest lower representable value,
	 *   chosen in an implementation-defined manner.  If the value being
	 *   converted is outside the range of values that can be represented,
	 *   the behavior is undefined.
	 */
#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
	return (duk_float_t) x;
#else
	duk_double_t t;

	t = DUK_FABS(x);
	DUK_ASSERT((DUK_ISNAN(x) && DUK_ISNAN(t)) ||
	           (!DUK_ISNAN(x) && !DUK_ISNAN(t)));

	if (DUK_LIKELY(t <= DUK__FLOAT_MAX)) {
		/* Standard in-range case, try to get here with a minimum
		 * number of checks and branches.
		 */
		DUK_ASSERT(!DUK_ISNAN(x));
		return (duk_float_t) x;
	} else if (t <= DUK__FLOAT_ROUND_LIMIT) {
		/* Out-of-range, but rounds to min/max float. */
		DUK_ASSERT(!DUK_ISNAN(x));
		if (x < 0.0) {
			return (duk_float_t) -DUK__FLOAT_MAX;
		} else {
			return (duk_float_t) DUK__FLOAT_MAX;
		}
	} else if (DUK_ISNAN(x)) {
		/* Assumes double NaN -> float NaN considered "in range". */
		DUK_ASSERT(DUK_ISNAN(x));
		return (duk_float_t) x;
	} else {
		/* Out-of-range, rounds to +/- Infinity. */
		if (x < 0.0) {
			return (duk_float_t) -DUK_DOUBLE_INFINITY;
		} else {
			return (duk_float_t) DUK_DOUBLE_INFINITY;
		}
	}
#endif
}

/* automatic undefs */
#undef DUK__DOUBLE_INT_CAST1
#undef DUK__DOUBLE_INT_CAST2
#undef DUK__DOUBLE_INT_CAST3
#undef DUK__DOUBLE_INT_CAST4
#undef DUK__FLOAT_MAX
#undef DUK__FLOAT_ROUND_LIMIT
#line 1 "duk_util_double.c"
/*
 *  IEEE double helpers.
 */

/* #include duk_internal.h -> already included */

DUK_INTERNAL duk_bool_t duk_double_is_anyinf(duk_double_t x) {
	duk_double_union du;
	du.d = x;
	return DUK_DBLUNION_IS_ANYINF(&du);
}

DUK_INTERNAL duk_bool_t duk_double_is_posinf(duk_double_t x) {
	duk_double_union du;
	du.d = x;
	return DUK_DBLUNION_IS_POSINF(&du);
}

DUK_INTERNAL duk_bool_t duk_double_is_neginf(duk_double_t x) {
	duk_double_union du;
	du.d = x;
	return DUK_DBLUNION_IS_NEGINF(&du);
}

DUK_INTERNAL duk_bool_t duk_double_is_nan(duk_double_t x) {
	duk_double_union du;
	du.d = x;
	/* Assumes we're dealing with a Duktape internal NaN which is
	 * NaN normalized if duk_tval requires it.
	 */
	DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
	return DUK_DBLUNION_IS_NAN(&du);
}

DUK_INTERNAL duk_bool_t duk_double_is_nan_or_zero(duk_double_t x) {
	duk_double_union du;
	du.d = x;
	/* Assumes we're dealing with a Duktape internal NaN which is
	 * NaN normalized if duk_tval requires it.
	 */
	DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
	return DUK_DBLUNION_IS_NAN(&du) || DUK_DBLUNION_IS_ANYZERO(&du);
}

DUK_INTERNAL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x) {
	duk_double_union du;
	du.d = x;
	/* If exponent is 0x7FF the argument is either a NaN or an
	 * infinity.  We don't need to check any other fields.
	 */
#if defined(DUK_USE_64BIT_OPS)
#if defined(DUK_USE_DOUBLE_ME)
	return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000);
#else
	return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000)) == DUK_U64_CONSTANT(0x7ff0000000000000);
#endif
#else
	return (du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL;
#endif
}

DUK_INTERNAL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x) {
	duk_double_union du;
#if defined(DUK_USE_64BIT_OPS)
	duk_uint64_t t;
#else
	duk_uint32_t t;
#endif
	du.d = x;
#if defined(DUK_USE_64BIT_OPS)
#if defined(DUK_USE_DOUBLE_ME)
	t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000);
	if (t == DUK_U64_CONSTANT(0x0000000000000000)) {
		t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x0000000080000000);
		return t == 0;
	}
	if (t == DUK_U64_CONSTANT(0x000000007ff00000)) {
		return 1;
	}
#else
	t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000);
	if (t == DUK_U64_CONSTANT(0x0000000000000000)) {
		t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x8000000000000000);
		return t == 0;
	}
	if (t == DUK_U64_CONSTANT(0x7ff0000000000000)) {
		return 1;
	}
#endif
#else
	t = du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL;
	if (t == 0x00000000UL) {
		return DUK_DBLUNION_IS_ANYZERO(&du);
	}
	if (t == 0x7ff00000UL) {
		return 1;
	}
#endif
	return 0;
}

DUK_INTERNAL duk_small_uint_t duk_double_signbit(duk_double_t x) {
	duk_double_union du;
	du.d = x;
	return (duk_small_uint_t) DUK_DBLUNION_GET_SIGNBIT(&du);
}

DUK_INTERNAL duk_double_t duk_double_trunc_towards_zero(duk_double_t x) {
	/* XXX: optimize */
	duk_small_uint_t s = duk_double_signbit(x);
	x = DUK_FLOOR(DUK_FABS(x));  /* truncate towards zero */
	if (s) {
		x = -x;
	}
	return x;
}

DUK_INTERNAL duk_bool_t duk_double_same_sign(duk_double_t x, duk_double_t y) {
	duk_double_union du1;
	duk_double_union du2;
	du1.d = x;
	du2.d = y;

	return (((du1.ui[DUK_DBL_IDX_UI0] ^ du2.ui[DUK_DBL_IDX_UI0]) & 0x80000000UL) == 0);
}

DUK_INTERNAL duk_double_t duk_double_fmin(duk_double_t x, duk_double_t y) {
	/* Doesn't replicate fmin() behavior exactly: for fmin() if one
	 * argument is a NaN, the other argument should be returned.
	 * Duktape doesn't rely on this behavior so the replacement can
	 * be simplified.
	 */
	return (x < y ? x : y);
}

DUK_INTERNAL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y) {
	/* Doesn't replicate fmax() behavior exactly: for fmax() if one
	 * argument is a NaN, the other argument should be returned.
	 * Duktape doesn't rely on this behavior so the replacement can
	 * be simplified.
	 */
	return (x > y ? x : y);
}

DUK_INTERNAL duk_bool_t duk_double_is_finite(duk_double_t x) {
	return !duk_double_is_nan_or_inf(x);
}

DUK_INTERNAL duk_bool_t duk_double_is_integer(duk_double_t x) {
	if (duk_double_is_nan_or_inf(x)) {
		return 0;
	} else {
		return duk_js_tointeger_number(x) == x;
	}
}

DUK_INTERNAL duk_bool_t duk_double_is_safe_integer(duk_double_t x) {
	/* >>> 2**53-1
	 * 9007199254740991
	 */
	return duk_double_is_integer(x) && DUK_FABS(x) <= 9007199254740991.0;
}

/* Check whether a duk_double_t is a whole number in the 32-bit range (reject
 * negative zero), and if so, return a duk_int32_t.
 * For compiler use: don't allow negative zero as it will cause trouble with
 * LDINT+LDINTX, positive zero is OK.
 */
DUK_INTERNAL duk_bool_t duk_is_whole_get_int32_nonegzero(duk_double_t x, duk_int32_t *ival) {
	duk_int32_t t;

	t = duk_double_to_int32_t(x);
	if (!((duk_double_t) t == x)) {
		return 0;
	}
	if (t == 0) {
		duk_double_union du;
		du.d = x;
		if (DUK_DBLUNION_HAS_SIGNBIT(&du)) {
			return 0;
		}
	}
	*ival = t;
	return 1;
}

/* Check whether a duk_double_t is a whole number in the 32-bit range, and if
 * so, return a duk_int32_t.
 */
DUK_INTERNAL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival) {
	duk_int32_t t;

	t = duk_double_to_int32_t(x);
	if (!((duk_double_t) t == x)) {
		return 0;
	}
	*ival = t;
	return 1;
}

/* Division: division by zero is undefined behavior (and may in fact trap)
 * so it needs special handling for portability.
 */

DUK_INTERNAL DUK_INLINE duk_double_t duk_double_div(duk_double_t x, duk_double_t y) {
#if !defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
	if (DUK_UNLIKELY(y == 0.0)) {
		/* In C99+ division by zero is undefined behavior so
		 * avoid it entirely.  Hopefully the compiler is
		 * smart enough to avoid emitting any actual code
		 * because almost all practical platforms behave as
		 * expected.
		 */
		if (x > 0.0) {
			if (DUK_SIGNBIT(y)) {
				return -DUK_DOUBLE_INFINITY;
			} else {
				return DUK_DOUBLE_INFINITY;
			}
		} else if (x < 0.0) {
			if (DUK_SIGNBIT(y)) {
				return DUK_DOUBLE_INFINITY;
			} else {
				return -DUK_DOUBLE_INFINITY;
			}
		} else {
			/* +/- 0, NaN */
			return DUK_DOUBLE_NAN;
		}
	}
#endif

	return x / y;
}
#line 1 "duk_util_hashbytes.c"
/*
 *  Hash function duk_util_hashbytes().
 *
 *  Currently, 32-bit MurmurHash2.
 *
 *  Don't rely on specific hash values; hash function may be endianness
 *  dependent, for instance.
 */

/* #include duk_internal.h -> already included */

#if defined(DUK_USE_STRHASH_DENSE)
/* 'magic' constants for Murmurhash2 */
#define DUK__MAGIC_M  ((duk_uint32_t) 0x5bd1e995UL)
#define DUK__MAGIC_R  24

DUK_INTERNAL duk_uint32_t duk_util_hashbytes(const duk_uint8_t *data, duk_size_t len, duk_uint32_t seed) {
	duk_uint32_t h = seed ^ ((duk_uint32_t) len);

	while (len >= 4) {
		/* Portability workaround is required for platforms without
		 * unaligned access.  The replacement code emulates little
		 * endian access even on big endian architectures, which is
		 * OK as long as it is consistent for a build.
		 */
#if defined(DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS)
		duk_uint32_t k = *((const duk_uint32_t *) (const void *) data);
#else
		duk_uint32_t k = ((duk_uint32_t) data[0]) |
		                 (((duk_uint32_t) data[1]) << 8) |
		                 (((duk_uint32_t) data[2]) << 16) |
		                 (((duk_uint32_t) data[3]) << 24);
#endif

		k *= DUK__MAGIC_M;
		k ^= k >> DUK__MAGIC_R;
		k *= DUK__MAGIC_M;
		h *= DUK__MAGIC_M;
		h ^= k;
		data += 4;
		len -= 4;
	}

	switch (len) {
	case 3: h ^= data[2] << 16;
	case 2: h ^= data[1] << 8;
	case 1: h ^= data[0];
	        h *= DUK__MAGIC_M;
        }

	h ^= h >> 13;
	h *= DUK__MAGIC_M;



( run in 0.593 second using v1.01-cache-2.11-cpan-ceb78f64989 )